@revealui/security 0.2.6 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,6 +9,7 @@ Security infrastructure for RevealUI. Provides HTTP security headers, CORS manag
9
9
  - You need audit logging for compliance (SOC2, HIPAA)
10
10
  - You need GDPR tooling: consent management, data export, breach reporting, anonymization
11
11
  - You need field-level encryption or key rotation
12
+ - You need to sanitize untrusted input before rendering (terminal streams, shell args, SQL identifiers)
12
13
 
13
14
  If you only need session auth (login/logout/password reset), use `@revealui/auth` instead.
14
15
 
@@ -83,14 +84,22 @@ Dependencies: `@revealui/contracts`, `@revealui/utils`
83
84
  | `InMemoryGDPRStorage` | Class | In-memory GDPR storage for testing |
84
85
  | `InMemoryBreachStorage` | Class | In-memory breach storage for testing |
85
86
 
87
+ ### Input Sanitization
88
+
89
+ | Export | Type | Purpose |
90
+ |--------|------|---------|
91
+ | `sanitizeTerminalLine` | Function | Strip ANSI escape sequences from untrusted terminal output; preserves SGR color codes, removes CSI/OSC/DCS sequences and C0/C1 control chars |
92
+
93
+ Used by RevDev Studio's terminal view to neutralize malicious output (e.g. cursor hijacking, title injection) before rendering. Consumers must treat all subprocess stdout/stderr as untrusted.
94
+
86
95
  ## JOSHUA Alignment
87
96
 
88
- - **Hermetic**: Security boundaries are sealedauth checks happen at middleware, never inside business logic
97
+ - **Hermetic**: Security boundaries are sealed - auth checks happen at middleware, never inside business logic
89
98
  - **Sovereign**: All security infrastructure runs in your deployment, no external auth service required
90
99
  - **Justifiable**: Every security header and policy has a documented reason (CSP prevents XSS, HSTS forces HTTPS, etc.)
91
100
 
92
101
  ## Related Packages
93
102
 
94
- - `@revealui/auth`Session-based authentication (login, password reset, OAuth)
95
- - `@revealui/core`Applies security middleware to CMS routes
96
- - `@revealui/contracts`Shared types for roles, permissions, consent records
103
+ - `@revealui/auth` - Session-based authentication (login, password reset, OAuth)
104
+ - `@revealui/core` - Applies security middleware to CMS routes
105
+ - `@revealui/contracts` - Shared types for roles, permissions, consent records
@@ -7,7 +7,7 @@ import {
7
7
  createAuditMiddleware,
8
8
  signAuditEntry,
9
9
  verifyAuditEntry
10
- } from "./chunk-Q5KAPSST.js";
10
+ } from "./chunk-F3P6YAJP.js";
11
11
  export {
12
12
  AuditReportGenerator,
13
13
  AuditSystem,
@@ -18,4 +18,3 @@ export {
18
18
  signAuditEntry,
19
19
  verifyAuditEntry
20
20
  };
21
- //# sourceMappingURL=audit-UF7PIYBU.js.map
@@ -315,6 +315,7 @@ var AuditReportGenerator = class {
315
315
  constructor(audit2) {
316
316
  this.audit = audit2;
317
317
  }
318
+ audit;
318
319
  /**
319
320
  * Generate security report
320
321
  */
@@ -426,4 +427,3 @@ export {
426
427
  verifyAuditEntry,
427
428
  audit
428
429
  };
429
- //# sourceMappingURL=chunk-Q5KAPSST.js.map
package/dist/index.d.ts CHANGED
@@ -198,7 +198,7 @@ interface SignableFields {
198
198
  * Compute an HMAC-SHA256 signature over the canonical fields of an audit entry.
199
199
  *
200
200
  * The signature covers `timestamp`, `eventType`, `severity`, `agentId`, and
201
- * `payload`the immutable core of every audit record. Changing any of
201
+ * `payload` - the immutable core of every audit record. Changing any of
202
202
  * these fields after signing will cause verification to fail.
203
203
  *
204
204
  * @param entry - The audit entry fields to sign
@@ -256,7 +256,7 @@ interface ThresholdRule {
256
256
  windowMs: number;
257
257
  /** Severity assigned to alerts from this rule. */
258
258
  severity: AuditSeverity;
259
- /** Human-readable message template`{count}` is replaced at runtime. */
259
+ /** Human-readable message template - `{count}` is replaced at runtime. */
260
260
  messageTemplate: string;
261
261
  }
262
262
  /** Top-level configuration for the alerting service. */
@@ -338,7 +338,7 @@ declare class SecurityAlertService {
338
338
  * Authentication Utilities
339
339
  *
340
340
  * OAuth support, password hashing, and two-factor authentication.
341
- * JWT-based auth was removedsession auth is handled by @revealui/auth.
341
+ * JWT-based auth was removed - session auth is handled by @revealui/auth.
342
342
  */
343
343
  interface User {
344
344
  id: string;
@@ -413,26 +413,14 @@ declare class OAuthClient {
413
413
  picture?: string;
414
414
  }>;
415
415
  }
416
- /**
417
- * Hash password with PBKDF2 and random salt
418
- */
419
- declare function hashPassword(password: string): Promise<string>;
420
- /**
421
- * Verify password against stored hash
422
- */
423
- declare function verifyPassword(password: string, storedHash: string): Promise<boolean>;
424
- declare const PasswordHasher: {
425
- readonly hash: typeof hashPassword;
426
- readonly verify: typeof verifyPassword;
427
- };
428
416
  /**
429
417
  * Generate TOTP secret
430
418
  */
431
419
  declare function generateSecret(): string;
432
420
  /**
433
421
  * Generate TOTP code (RFC 6238 compliant).
434
- * Secret is base32-encodeddecoded before HMAC.
435
- * Counter is encoded as 8-byte big-endianmatches all standard authenticator apps.
422
+ * Secret is base32-encoded - decoded before HMAC.
423
+ * Counter is encoded as 8-byte big-endian - matches all standard authenticator apps.
436
424
  */
437
425
  declare function generateCode(secret: string, timestamp?: number): string;
438
426
  /**
@@ -567,7 +555,7 @@ declare class AuthorizationSystem {
567
555
  */
568
556
  declare const authorization: AuthorizationSystem;
569
557
  /**
570
- * Common rolesaligned with DB schema (`users.role` column)
558
+ * Common roles - aligned with DB schema (`users.role` column)
571
559
  * and `UserRoleSchema` in @revealui/contracts.
572
560
  *
573
561
  * Values: owner | admin | editor | viewer | agent | contributor
@@ -957,7 +945,7 @@ interface BreachStorage {
957
945
  * In-memory implementation of `BreachStorage`.
958
946
  *
959
947
  * WARNING: All data is lost on process restart or serverless cold start.
960
- * GDPR requires breach records be retaineduse database-backed storage in production.
948
+ * GDPR requires breach records be retained - use database-backed storage in production.
961
949
  */
962
950
  declare class InMemoryBreachStorage implements BreachStorage {
963
951
  private breaches;
@@ -1154,7 +1142,7 @@ declare function anonymizeUser(user: Record<string, unknown>): Record<string, un
1154
1142
  /**
1155
1143
  * Pseudonymize data (one-way, key-dependent)
1156
1144
  *
1157
- * Uses HMAC-SHA256cryptographically bound to the key, resistant to
1145
+ * Uses HMAC-SHA256 - cryptographically bound to the key, resistant to
1158
1146
  * length-extension attacks and GPU brute-force (unlike plain SHA-256).
1159
1147
  */
1160
1148
  declare function pseudonymize(value: string, key: string): string;
@@ -1291,7 +1279,7 @@ declare class DataBreachManager {
1291
1279
  * Factory functions for GDPR subsystems.
1292
1280
  *
1293
1281
  * `ConsentManager` and `DataDeletionSystem` require a `GDPRStorage` implementation.
1294
- * Use `InMemoryGDPRStorage` only in testsproduction MUST use a database-backed store.
1282
+ * Use `InMemoryGDPRStorage` only in tests - production MUST use a database-backed store.
1295
1283
  *
1296
1284
  * `DataExportSystem`, `PrivacyPolicyManager`, `CookieConsentManager`, and
1297
1285
  * `DataBreachManager` are stateless or client-side only, so singletons are safe.
@@ -1468,12 +1456,12 @@ declare const CORSPresets: {
1468
1456
  */
1469
1457
  moderate: (allowedOrigins: string[]) => CORSConfig;
1470
1458
  /**
1471
- * Permissive CORS (all origins)development only.
1459
+ * Permissive CORS (all origins) - development only.
1472
1460
  * Logs a warning if used when NODE_ENV === 'production'.
1473
1461
  */
1474
1462
  permissive: () => CORSConfig;
1475
1463
  /**
1476
- * API CORS (public read-only APIs)credentials disabled.
1464
+ * API CORS (public read-only APIs) - credentials disabled.
1477
1465
  * Logs a warning if used when NODE_ENV === 'production'.
1478
1466
  */
1479
1467
  api: () => CORSConfig;
@@ -1504,4 +1492,120 @@ interface SecurityLogger {
1504
1492
  */
1505
1493
  declare function configureSecurityLogger(logger: SecurityLogger): void;
1506
1494
 
1507
- export { type AlertHandler, type AlertingConfig, AuditAlertHandler, type AuditEvent, type AuditEventType, type AuditQuery, AuditReportGenerator, type AuditSeverity, type AuditStorage, AuditSystem, AuditTrail, type AuthorizationContext, AuthorizationSystem, type BreachStorage, type CORSConfig, CORSManager, CORSPresets, CommonRoles, ConsentManager, type ConsentRecord, type ConsentType, type ContentSecurityPolicyConfig, type CookieConsentConfig, CookieConsentManager, DEFAULT_THRESHOLDS, DataAnonymization, type DataBreach, DataBreachManager, type DataCategory, type DataDeletionRequest, DataDeletionSystem, DataExportSystem, DataMasking, type DataProcessingPurpose, type EncryptedData, type EncryptionConfig, EncryptionSystem, EnvelopeEncryption, FieldEncryption, type GDPRStorage, type HSTSConfig, InMemoryAuditStorage, InMemoryBreachStorage, InMemoryGDPRStorage, KeyRotationManager, LogAlertHandler, OAuthClient, type OAuthConfig, OAuthProviders, PasswordHasher, type Permission, PermissionBuilder, PermissionCache, type PermissionsPolicyConfig, type PersonalDataExport, type Policy, PolicyBuilder, type PolicyCondition, PrivacyPolicyManager, type ReferrerPolicyValue, RequirePermission, RequireRole, type Role, type SecurityAlert, SecurityAlertService, SecurityHeaders, type SecurityHeadersConfig, type SecurityLogger, SecurityPresets, type ThresholdRule, TokenGenerator, TwoFactorAuth, type User, WebhookAlertHandler, audit, authorization, canAccessResource, checkAttributeAccess, configureSecurityLogger, cookieConsentManager, createAuditMiddleware, createAuthorizationMiddleware, createConsentManager, createDataBreachManager, createDataDeletionSystem, createSecurityMiddleware, dataExportSystem, encryption, permissionCache, privacyPolicyManager, setRateLimitHeaders, signAuditEntry, verifyAuditEntry };
1495
+ /**
1496
+ * Input-sanitization primitives for untrusted strings heading into a
1497
+ * control-sequence-sensitive sink (terminal, URL, HTML, shell, etc.).
1498
+ *
1499
+ * Scope: call these at the point a string crosses into a sink that parses
1500
+ * control bytes. Do not pre-sanitize at data ingress — sanitize for the
1501
+ * output context, where the threat model is concrete.
1502
+ */
1503
+ /**
1504
+ * Sanitize a string destined for a terminal banner / welcome sink.
1505
+ * Preserves SGR colour + attribute escapes, strips every other control
1506
+ * byte and ANSI sequence family.
1507
+ *
1508
+ * Why: untrusted ANSI is a known terminal-escape-injection surface
1509
+ * (cursor hijack, window-title rewrite, OSC-8 hyperlink spoofing). Use
1510
+ * this for any string the app writes to a terminal that did not come
1511
+ * directly from a trusted PTY.
1512
+ */
1513
+ declare function sanitizeTerminalLine(input: string): string;
1514
+ type ShellDialect = 'posix' | 'cmd' | 'powershell';
1515
+ /**
1516
+ * Quote an untrusted string so it traverses a shell as a single literal
1517
+ * argv token, with no metacharacter interpretation.
1518
+ *
1519
+ * Use this only when a real shell is unavoidable. For local `spawn()`
1520
+ * calls, pass an argv array instead and skip shell parsing entirely.
1521
+ *
1522
+ * @param arg - The untrusted value to embed.
1523
+ * @param shell - `'posix'` (default) for sh/bash/zsh, `'cmd'` for
1524
+ * Windows cmd.exe, `'powershell'` for PowerShell.
1525
+ * @throws If `arg` contains a NUL byte (which every shell treats as an
1526
+ * argument terminator — no safe encoding exists).
1527
+ */
1528
+ declare function escapeShellArg(arg: string, shell?: ShellDialect): string;
1529
+ /**
1530
+ * Quote + escape a Postgres identifier for safe interpolation into raw
1531
+ * SQL. Returns `"name"`, with embedded double-quotes doubled.
1532
+ *
1533
+ * Throws on empty input, NUL bytes, or anything over 63 bytes — the
1534
+ * three failure modes where silent acceptance would produce a
1535
+ * syntactically valid but semantically wrong query.
1536
+ *
1537
+ * Prefer Drizzle's `sql.identifier()` for compile-time-known names;
1538
+ * reach for this only when the identifier truly has to flow through
1539
+ * user input or runtime configuration.
1540
+ */
1541
+ declare function escapeSqlIdentifier(identifier: string): string;
1542
+ type UrlContext = 'link' | 'image';
1543
+ /**
1544
+ * Return `true` if the URL is safe to render in the given context.
1545
+ *
1546
+ * - `link` (default): http(s), mailto:, tel:, fragment, relative path.
1547
+ * - `image`: same as link, plus `data:image/…` for inline base64 images.
1548
+ *
1549
+ * Blocks `javascript:` / `vbscript:` / non-image `data:`, including
1550
+ * leading-whitespace evasions (` javascript:…`) and mixed-case
1551
+ * (`JaVaScRiPt:…`). Unknown schemes are blocked by default — allow-list,
1552
+ * not deny-list.
1553
+ */
1554
+ declare function isSafeUrl(url: string, context?: UrlContext): boolean;
1555
+ /**
1556
+ * Sanitize a URL for rendering. Returns the trimmed input if safe,
1557
+ * otherwise `'#'` — a harmless anchor that renders without navigation.
1558
+ */
1559
+ declare function sanitizeUrl(url: string, context?: UrlContext): string;
1560
+ interface SanitizeHtmlOptions {
1561
+ /** Additional tag names allowed on top of the default set. Lower-case. */
1562
+ readonly extraTags?: readonly string[];
1563
+ /** Additional per-tag attributes, keyed by lower-case tag name. */
1564
+ readonly extraAttrs?: Readonly<Record<string, readonly string[]>>;
1565
+ }
1566
+ /**
1567
+ * Sanitize an untrusted HTML string against a tag + attribute allow-list.
1568
+ *
1569
+ * Safe to render the result via `dangerouslySetInnerHTML` or direct
1570
+ * `innerHTML=`. Known-dangerous containers (script, style, iframe, etc.)
1571
+ * are dropped with their contents; unknown tags are unwrapped; every
1572
+ * `on*` event-handler attribute is stripped; URL attributes (`href`,
1573
+ * `src`, `cite`) are filtered through `isSafeUrl`.
1574
+ *
1575
+ * For Lexical / markdown render paths — sanitize at the sink.
1576
+ */
1577
+ declare function sanitizeHtml(input: string, options?: SanitizeHtmlOptions): string;
1578
+ declare const REDACTED: "[REDACTED]";
1579
+ /**
1580
+ * `true` if `key` names a class of value that must never reach a log.
1581
+ * Match is case-insensitive substring so variants like `userApiKey`,
1582
+ * `X-API-KEY`, `apikey`, `sessionId` all resolve to the same class.
1583
+ */
1584
+ declare function isSensitiveLogKey(key: string): boolean;
1585
+ /**
1586
+ * Scrub inline secret shapes (JWT, Bearer headers, provider API keys)
1587
+ * from an arbitrary string — for log messages, error messages, and
1588
+ * anything else that may have been concatenated from untrusted sources.
1589
+ * Returns the original string if nothing matched.
1590
+ */
1591
+ declare function redactSecretsInString(input: string): string;
1592
+ /**
1593
+ * Decide the safe form of a single log field.
1594
+ *
1595
+ * - If `key` is sensitive: returns `REDACTED` regardless of value shape.
1596
+ * - If `value` is a string: returns it with inline secret shapes scrubbed.
1597
+ * - Otherwise: returns `value` unchanged. Nested objects/arrays are the
1598
+ * caller's responsibility — use `redactLogContext` to walk a tree.
1599
+ */
1600
+ declare function redactLogField(key: string, value: unknown): unknown;
1601
+ /**
1602
+ * Recursively redact a log context object. Walks plain objects and
1603
+ * arrays; leaves Dates, Errors, Maps, Sets, typed arrays, and other
1604
+ * non-plain objects untouched (stringifying them is the logger's job).
1605
+ *
1606
+ * Depth is capped at 8 to avoid pathological payloads — deeper levels
1607
+ * are replaced with `REDACTED` rather than recursed into.
1608
+ */
1609
+ declare function redactLogContext<T>(obj: T): T;
1610
+
1611
+ export { type AlertHandler, type AlertingConfig, AuditAlertHandler, type AuditEvent, type AuditEventType, type AuditQuery, AuditReportGenerator, type AuditSeverity, type AuditStorage, AuditSystem, AuditTrail, type AuthorizationContext, AuthorizationSystem, type BreachStorage, type CORSConfig, CORSManager, CORSPresets, CommonRoles, ConsentManager, type ConsentRecord, type ConsentType, type ContentSecurityPolicyConfig, type CookieConsentConfig, CookieConsentManager, DEFAULT_THRESHOLDS, DataAnonymization, type DataBreach, DataBreachManager, type DataCategory, type DataDeletionRequest, DataDeletionSystem, DataExportSystem, DataMasking, type DataProcessingPurpose, type EncryptedData, type EncryptionConfig, EncryptionSystem, EnvelopeEncryption, FieldEncryption, type GDPRStorage, type HSTSConfig, InMemoryAuditStorage, InMemoryBreachStorage, InMemoryGDPRStorage, KeyRotationManager, LogAlertHandler, OAuthClient, type OAuthConfig, OAuthProviders, type Permission, PermissionBuilder, PermissionCache, type PermissionsPolicyConfig, type PersonalDataExport, type Policy, PolicyBuilder, type PolicyCondition, PrivacyPolicyManager, REDACTED, type ReferrerPolicyValue, RequirePermission, RequireRole, type Role, type SanitizeHtmlOptions, type SecurityAlert, SecurityAlertService, SecurityHeaders, type SecurityHeadersConfig, type SecurityLogger, SecurityPresets, type ShellDialect, type ThresholdRule, TokenGenerator, TwoFactorAuth, type UrlContext, type User, WebhookAlertHandler, audit, authorization, canAccessResource, checkAttributeAccess, configureSecurityLogger, cookieConsentManager, createAuditMiddleware, createAuthorizationMiddleware, createConsentManager, createDataBreachManager, createDataDeletionSystem, createSecurityMiddleware, dataExportSystem, encryption, escapeShellArg, escapeSqlIdentifier, isSafeUrl, isSensitiveLogKey, permissionCache, privacyPolicyManager, redactLogContext, redactLogField, redactSecretsInString, sanitizeHtml, sanitizeTerminalLine, sanitizeUrl, setRateLimitHeaders, signAuditEntry, verifyAuditEntry };
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  createAuditMiddleware,
8
8
  signAuditEntry,
9
9
  verifyAuditEntry
10
- } from "./chunk-Q5KAPSST.js";
10
+ } from "./chunk-F3P6YAJP.js";
11
11
 
12
12
  // src/logger.ts
13
13
  var securityLogger = console;
@@ -67,7 +67,7 @@ var AuditAlertHandler = class {
67
67
  /** Record the alert in the audit trail with severity 'critical'. */
68
68
  async handle(alert) {
69
69
  try {
70
- const { audit: audit2 } = await import("./audit-UF7PIYBU.js");
70
+ const { audit: audit2 } = await import("./audit-DA3LOQLL.js");
71
71
  await audit2.logSecurityEvent(
72
72
  "alert",
73
73
  "critical",
@@ -321,44 +321,6 @@ var OAuthClient = class {
321
321
  return response.json();
322
322
  }
323
323
  };
324
- var PH_ITERATIONS = 1e5;
325
- var PH_KEY_LENGTH = 64;
326
- var PH_DIGEST = "sha512";
327
- async function hashPassword(password) {
328
- const { pbkdf2, randomBytes: rb } = await import("crypto");
329
- const salt = rb(16).toString("hex");
330
- return new Promise((resolve, reject) => {
331
- pbkdf2(password, salt, PH_ITERATIONS, PH_KEY_LENGTH, PH_DIGEST, (err, derivedKey) => {
332
- if (err) reject(err);
333
- else resolve(`${salt}:${derivedKey.toString("hex")}`);
334
- });
335
- });
336
- }
337
- async function verifyPassword(password, storedHash) {
338
- const { pbkdf2, timingSafeEqual: tse } = await import("crypto");
339
- const [salt, hash] = storedHash.split(":");
340
- if (!(salt && hash)) {
341
- return false;
342
- }
343
- return new Promise((resolve, reject) => {
344
- pbkdf2(password, salt, PH_ITERATIONS, PH_KEY_LENGTH, PH_DIGEST, (err, derivedKey) => {
345
- if (err) reject(err);
346
- else {
347
- const derived = Buffer.from(derivedKey.toString("hex"), "utf-8");
348
- const expected = Buffer.from(hash, "utf-8");
349
- if (derived.length !== expected.length) {
350
- resolve(false);
351
- } else {
352
- resolve(tse(derived, expected));
353
- }
354
- }
355
- });
356
- });
357
- }
358
- var PasswordHasher = {
359
- hash: hashPassword,
360
- verify: verifyPassword
361
- };
362
324
  function base32Encode(buffer) {
363
325
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
364
326
  let result = "";
@@ -380,7 +342,10 @@ function base32Encode(buffer) {
380
342
  }
381
343
  function base32Decode(encoded) {
382
344
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
383
- const stripped = encoded.replace(/=+$/, "").toUpperCase();
345
+ let stripped = encoded.toUpperCase();
346
+ let end = stripped.length;
347
+ while (end > 0 && stripped[end - 1] === "=") end--;
348
+ stripped = stripped.slice(0, end);
384
349
  const bytes = [];
385
350
  let bits = 0;
386
351
  let value = 0;
@@ -533,7 +498,7 @@ var AuthorizationSystem = class {
533
498
  if (pattern === "*") return true;
534
499
  if (pattern === resource) return true;
535
500
  const regex = new RegExp(
536
- `^${pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".")}$`
501
+ `^${pattern.replace(/\\/g, "\\\\").replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".")}$`
537
502
  );
538
503
  return regex.test(resource);
539
504
  }
@@ -544,7 +509,7 @@ var AuthorizationSystem = class {
544
509
  if (pattern === "*") return true;
545
510
  if (pattern === action) return true;
546
511
  const regex = new RegExp(
547
- `^${pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".")}$`
512
+ `^${pattern.replace(/\\/g, "\\\\").replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".")}$`
548
513
  );
549
514
  return regex.test(action);
550
515
  }
@@ -616,7 +581,7 @@ var CommonRoles = {
616
581
  owner: {
617
582
  id: "owner",
618
583
  name: "Owner",
619
- description: "Full control \u2014 inherits admin",
584
+ description: "Full control - inherits admin",
620
585
  permissions: [{ resource: "*", action: "*" }],
621
586
  inherits: ["admin"]
622
587
  },
@@ -666,7 +631,7 @@ var CommonRoles = {
666
631
  contributor: {
667
632
  id: "contributor",
668
633
  name: "Contributor",
669
- description: "Can suggest changes \u2014 create drafts but not publish or delete",
634
+ description: "Can suggest changes - create drafts but not publish or delete",
670
635
  permissions: [
671
636
  { resource: "content", action: "read" },
672
637
  { resource: "content", action: "create" },
@@ -900,7 +865,7 @@ var EncryptionSystem = class {
900
865
  length: this.config.keySize
901
866
  },
902
867
  this.config.extractable ?? false,
903
- // non-extractable by defaultprevents key exfiltration
868
+ // non-extractable by default - prevents key exfiltration
904
869
  ["encrypt", "decrypt"]
905
870
  );
906
871
  if (keyId) {
@@ -924,7 +889,7 @@ var EncryptionSystem = class {
924
889
  length: this.config.keySize
925
890
  },
926
891
  this.config.extractable ?? false,
927
- // non-extractable by defaultprevents key exfiltration
892
+ // non-extractable by default - prevents key exfiltration
928
893
  ["encrypt", "decrypt"]
929
894
  );
930
895
  if (keyId) {
@@ -2160,13 +2125,13 @@ var CORSPresets = {
2160
2125
  maxAge: 86400
2161
2126
  }),
2162
2127
  /**
2163
- * Permissive CORS (all origins)development only.
2128
+ * Permissive CORS (all origins) - development only.
2164
2129
  * Logs a warning if used when NODE_ENV === 'production'.
2165
2130
  */
2166
2131
  permissive: () => {
2167
2132
  if (process.env.NODE_ENV === "production") {
2168
2133
  getSecurityLogger().warn(
2169
- "[SecurityPresets] CORS permissive preset used in production \u2014 this allows all origins. Use moderate() with explicit origins instead."
2134
+ "[SecurityPresets] CORS permissive preset used in production - this allows all origins. Use moderate() with explicit origins instead."
2170
2135
  );
2171
2136
  }
2172
2137
  return {
@@ -2178,7 +2143,7 @@ var CORSPresets = {
2178
2143
  };
2179
2144
  },
2180
2145
  /**
2181
- * API CORS (public read-only APIs)credentials disabled.
2146
+ * API CORS (public read-only APIs) - credentials disabled.
2182
2147
  * Logs a warning if used when NODE_ENV === 'production'.
2183
2148
  */
2184
2149
  api: () => {
@@ -2221,6 +2186,340 @@ function setRateLimitHeaders(response, limit, remaining, reset) {
2221
2186
  response.headers.set("X-RateLimit-Remaining", remaining.toString());
2222
2187
  response.headers.set("X-RateLimit-Reset", reset.toString());
2223
2188
  }
2189
+
2190
+ // src/sanitize.ts
2191
+ import { defaultTreeAdapter, parseFragment, serialize } from "parse5";
2192
+ var ANY_TERMINAL_ESCAPE = /\x1b\](?:[^\x07\x1b]*)(?:\x07|\x1b\\)?|\x1b[PX^_](?:[^\x1b]*)(?:\x1b\\)?|\x1b\[[0-?]*[ -/]*[@-~]|\x1b.|\x1b/g;
2193
+ var SGR_CSI = /^\x1b\[[0-?]*[ -/]*m$/;
2194
+ var DISALLOWED_TERMINAL_CONTROL = /[\x00-\x08\x0b\x0c\x0e-\x1a\x1c-\x1f\x7f]/g;
2195
+ function sanitizeTerminalLine(input) {
2196
+ const stripped = input.replace(
2197
+ ANY_TERMINAL_ESCAPE,
2198
+ (match) => SGR_CSI.test(match) ? match : ""
2199
+ );
2200
+ return stripped.replace(DISALLOWED_TERMINAL_CONTROL, "");
2201
+ }
2202
+ function escapePosix(arg) {
2203
+ return `'${arg.replace(/'/g, `'\\''`)}'`;
2204
+ }
2205
+ function escapeCmd(arg) {
2206
+ const quoted = `"${arg.replace(/"/g, '""')}"`;
2207
+ return quoted.replace(/([&|<>^()%!])/g, "^$1");
2208
+ }
2209
+ function escapePowerShell(arg) {
2210
+ return `'${arg.replace(/'/g, `''`)}'`;
2211
+ }
2212
+ function escapeShellArg(arg, shell = "posix") {
2213
+ if (arg.includes("\0")) {
2214
+ throw new Error("escapeShellArg: NUL byte in argument \u2014 no shell can represent it");
2215
+ }
2216
+ switch (shell) {
2217
+ case "posix":
2218
+ return escapePosix(arg);
2219
+ case "cmd":
2220
+ return escapeCmd(arg);
2221
+ case "powershell":
2222
+ return escapePowerShell(arg);
2223
+ default: {
2224
+ const _exhaustive = shell;
2225
+ throw new Error(`escapeShellArg: unknown shell dialect ${String(_exhaustive)}`);
2226
+ }
2227
+ }
2228
+ }
2229
+ var MAX_PG_IDENTIFIER_BYTES = 63;
2230
+ function escapeSqlIdentifier(identifier) {
2231
+ if (identifier === "") {
2232
+ throw new Error("escapeSqlIdentifier: identifier must not be empty");
2233
+ }
2234
+ if (identifier.includes("\0")) {
2235
+ throw new Error("escapeSqlIdentifier: identifier contains NUL byte");
2236
+ }
2237
+ const byteLength = Buffer.byteLength(identifier, "utf8");
2238
+ if (byteLength > MAX_PG_IDENTIFIER_BYTES) {
2239
+ throw new Error(
2240
+ `escapeSqlIdentifier: identifier exceeds ${MAX_PG_IDENTIFIER_BYTES}-byte limit (got ${byteLength} bytes) \u2014 Postgres silently truncates longer names`
2241
+ );
2242
+ }
2243
+ return `"${identifier.split('"').join('""')}"`;
2244
+ }
2245
+ var SAFE_LINK_PROTOCOLS = /^(?:https?:|mailto:|tel:|#|\/)/i;
2246
+ var SAFE_IMAGE_DATA_URI = /^data:image\//i;
2247
+ var DANGEROUS_SCRIPT_PROTOCOL = /^(?:javascript|vbscript):/i;
2248
+ var ANY_DATA_URI = /^data:/i;
2249
+ function isSafeUrl(url, context = "link") {
2250
+ const trimmed = url.trim();
2251
+ if (trimmed === "" || trimmed === "#") {
2252
+ return true;
2253
+ }
2254
+ if (context === "image" && SAFE_IMAGE_DATA_URI.test(trimmed)) {
2255
+ return true;
2256
+ }
2257
+ if (ANY_DATA_URI.test(trimmed)) {
2258
+ return false;
2259
+ }
2260
+ if (DANGEROUS_SCRIPT_PROTOCOL.test(trimmed)) {
2261
+ return false;
2262
+ }
2263
+ if (SAFE_LINK_PROTOCOLS.test(trimmed) || !trimmed.includes(":")) {
2264
+ return true;
2265
+ }
2266
+ return false;
2267
+ }
2268
+ function sanitizeUrl(url, context = "link") {
2269
+ return isSafeUrl(url, context) ? url.trim() : "#";
2270
+ }
2271
+ var DEFAULT_ALLOWED_TAGS = /* @__PURE__ */ new Set([
2272
+ "a",
2273
+ "b",
2274
+ "blockquote",
2275
+ "br",
2276
+ "code",
2277
+ "div",
2278
+ "em",
2279
+ "h1",
2280
+ "h2",
2281
+ "h3",
2282
+ "h4",
2283
+ "h5",
2284
+ "h6",
2285
+ "hr",
2286
+ "i",
2287
+ "img",
2288
+ "li",
2289
+ "ol",
2290
+ "p",
2291
+ "pre",
2292
+ "s",
2293
+ "span",
2294
+ "strong",
2295
+ "sub",
2296
+ "sup",
2297
+ "table",
2298
+ "tbody",
2299
+ "td",
2300
+ "tfoot",
2301
+ "th",
2302
+ "thead",
2303
+ "tr",
2304
+ "u",
2305
+ "ul"
2306
+ ]);
2307
+ var DANGEROUS_CONTAINER_TAGS = /* @__PURE__ */ new Set([
2308
+ "applet",
2309
+ "base",
2310
+ "body",
2311
+ "embed",
2312
+ "form",
2313
+ "frame",
2314
+ "frameset",
2315
+ "head",
2316
+ "html",
2317
+ "iframe",
2318
+ "input",
2319
+ "link",
2320
+ "math",
2321
+ "meta",
2322
+ "noembed",
2323
+ "noframes",
2324
+ "noscript",
2325
+ "object",
2326
+ "script",
2327
+ "select",
2328
+ "style",
2329
+ "svg",
2330
+ "template",
2331
+ "textarea",
2332
+ "title",
2333
+ "xml"
2334
+ ]);
2335
+ var GLOBAL_ATTRS = /* @__PURE__ */ new Set(["class", "id", "title", "lang", "dir"]);
2336
+ var PER_TAG_ATTRS = {
2337
+ a: /* @__PURE__ */ new Set(["href", "target", "rel", "name"]),
2338
+ img: /* @__PURE__ */ new Set(["src", "alt", "width", "height", "loading"]),
2339
+ td: /* @__PURE__ */ new Set(["colspan", "rowspan", "align", "valign"]),
2340
+ th: /* @__PURE__ */ new Set(["colspan", "rowspan", "align", "valign", "scope"]),
2341
+ ol: /* @__PURE__ */ new Set(["start", "reversed", "type"]),
2342
+ li: /* @__PURE__ */ new Set(["value"]),
2343
+ code: /* @__PURE__ */ new Set(["data-language"]),
2344
+ pre: /* @__PURE__ */ new Set(["data-language"]),
2345
+ blockquote: /* @__PURE__ */ new Set(["cite"])
2346
+ };
2347
+ var URL_ATTRS = {
2348
+ href: "link",
2349
+ src: "image",
2350
+ cite: "link"
2351
+ };
2352
+ function sanitizeHtml(input, options) {
2353
+ const allowedTags = new Set(DEFAULT_ALLOWED_TAGS);
2354
+ if (options?.extraTags) {
2355
+ for (const t of options.extraTags) allowedTags.add(t.toLowerCase());
2356
+ }
2357
+ const extraAttrs = options?.extraAttrs ?? {};
2358
+ const fragment = parseFragment(input);
2359
+ filterChildren(fragment, allowedTags, extraAttrs);
2360
+ return serialize(fragment);
2361
+ }
2362
+ function filterChildren(parent, allowedTags, extraAttrs) {
2363
+ const kept = [];
2364
+ for (const node of parent.childNodes) {
2365
+ const next = filterNode(node, allowedTags, extraAttrs);
2366
+ for (const n of next) {
2367
+ n.parentNode = parent;
2368
+ kept.push(n);
2369
+ }
2370
+ }
2371
+ parent.childNodes = kept;
2372
+ }
2373
+ function filterNode(node, allowedTags, extraAttrs) {
2374
+ if (defaultTreeAdapter.isElementNode(node)) {
2375
+ const tag = node.tagName.toLowerCase();
2376
+ if (DANGEROUS_CONTAINER_TAGS.has(tag)) {
2377
+ return [];
2378
+ }
2379
+ filterChildren(node, allowedTags, extraAttrs);
2380
+ if (!allowedTags.has(tag)) {
2381
+ return node.childNodes.slice();
2382
+ }
2383
+ node.attrs = filterAttrs(tag, node.attrs, extraAttrs);
2384
+ hardenAnchor(tag, node);
2385
+ return [node];
2386
+ }
2387
+ if (defaultTreeAdapter.isTextNode(node)) {
2388
+ return [node];
2389
+ }
2390
+ return [];
2391
+ }
2392
+ function filterAttrs(tag, attrs, extraAttrs) {
2393
+ const tagAttrs = PER_TAG_ATTRS[tag];
2394
+ const extraForTag = extraAttrs[tag];
2395
+ const out = [];
2396
+ for (const attr of attrs) {
2397
+ const name = attr.name.toLowerCase();
2398
+ if (name.startsWith("on")) continue;
2399
+ if (name === "style") continue;
2400
+ if (name === "srcdoc") continue;
2401
+ if (name === "xmlns" || name.startsWith("xmlns:")) continue;
2402
+ if (name.includes(":")) continue;
2403
+ const allowed = GLOBAL_ATTRS.has(name) || tagAttrs?.has(name) || extraForTag?.includes(name) || name.startsWith("data-") || name.startsWith("aria-");
2404
+ if (!allowed) continue;
2405
+ if (name in URL_ATTRS) {
2406
+ const context = URL_ATTRS[name];
2407
+ if (context === void 0 || !isSafeUrl(attr.value, context)) continue;
2408
+ out.push({ ...attr, name, value: attr.value.trim() });
2409
+ continue;
2410
+ }
2411
+ out.push({ ...attr, name });
2412
+ }
2413
+ return out;
2414
+ }
2415
+ function hardenAnchor(tag, node) {
2416
+ if (tag !== "a") return;
2417
+ const target = node.attrs.find((a) => a.name === "target");
2418
+ if (!target || target.value !== "_blank") return;
2419
+ const rel = node.attrs.find((a) => a.name === "rel");
2420
+ const tokens = new Set((rel?.value ?? "").split(/\s+/).filter(Boolean));
2421
+ tokens.add("noopener");
2422
+ tokens.add("noreferrer");
2423
+ const merged = Array.from(tokens).join(" ");
2424
+ if (rel) {
2425
+ rel.value = merged;
2426
+ } else {
2427
+ node.attrs.push({ name: "rel", value: merged });
2428
+ }
2429
+ }
2430
+ var REDACTED = "[REDACTED]";
2431
+ var SENSITIVE_KEY_SUBSTRINGS = [
2432
+ "password",
2433
+ "passwd",
2434
+ "pwd",
2435
+ "secret",
2436
+ "token",
2437
+ "apikey",
2438
+ "authorization",
2439
+ "cookie",
2440
+ "session",
2441
+ "privatekey",
2442
+ "encryptedkey",
2443
+ "creditcard",
2444
+ "cardnumber",
2445
+ "cvv",
2446
+ "cvc",
2447
+ "ssn"
2448
+ ];
2449
+ var NON_ALNUM = /[^a-z0-9]/g;
2450
+ var SECRET_VALUE_PATTERNS = [
2451
+ // JWT (header.payload.signature) — base64url segments, min lengths keep
2452
+ // this from matching arbitrary dotted identifiers.
2453
+ /eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g,
2454
+ // Bearer <token> in header-style strings.
2455
+ /\b[Bb]earer\s+[A-Za-z0-9._~+/-]{16,}=*/g,
2456
+ // OpenAI: sk-…, sk-proj-…, sk-svcacct-…
2457
+ /\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{20,}/g,
2458
+ // Stripe secret + restricted keys.
2459
+ /\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{20,}/g,
2460
+ // Stripe webhook signing secret.
2461
+ /\bwhsec_[A-Za-z0-9]{20,}/g,
2462
+ // AWS access key id.
2463
+ /\bAKIA[0-9A-Z]{16}\b/g,
2464
+ // GitHub classic PAT (36+ char suffix) and fine-grained token.
2465
+ /\bghp_[A-Za-z0-9]{20,}/g,
2466
+ /\bgithub_pat_[A-Za-z0-9_]{20,}/g
2467
+ ];
2468
+ function isSensitiveLogKey(key) {
2469
+ const normalised = key.toLowerCase().replace(NON_ALNUM, "");
2470
+ for (const needle of SENSITIVE_KEY_SUBSTRINGS) {
2471
+ if (normalised.includes(needle)) return true;
2472
+ }
2473
+ return false;
2474
+ }
2475
+ function redactSecretsInString(input) {
2476
+ let out = input;
2477
+ for (const pattern of SECRET_VALUE_PATTERNS) {
2478
+ out = out.replace(pattern, REDACTED);
2479
+ }
2480
+ return out;
2481
+ }
2482
+ function redactLogField(key, value) {
2483
+ if (isSensitiveLogKey(key)) {
2484
+ return REDACTED;
2485
+ }
2486
+ if (typeof value === "string") {
2487
+ return redactSecretsInString(value);
2488
+ }
2489
+ return value;
2490
+ }
2491
+ var MAX_REDACT_DEPTH = 8;
2492
+ function redactLogContext(obj) {
2493
+ return walk(obj, 0);
2494
+ }
2495
+ function isPlainObject(v) {
2496
+ if (v === null || typeof v !== "object") return false;
2497
+ const proto = Object.getPrototypeOf(v);
2498
+ return proto === Object.prototype || proto === null;
2499
+ }
2500
+ function walk(value, depth) {
2501
+ if (depth >= MAX_REDACT_DEPTH) {
2502
+ return isPlainObject(value) || Array.isArray(value) ? REDACTED : value;
2503
+ }
2504
+ if (typeof value === "string") {
2505
+ return redactSecretsInString(value);
2506
+ }
2507
+ if (Array.isArray(value)) {
2508
+ return value.map((item) => walk(item, depth + 1));
2509
+ }
2510
+ if (isPlainObject(value)) {
2511
+ const out = {};
2512
+ for (const [k, v] of Object.entries(value)) {
2513
+ if (isSensitiveLogKey(k)) {
2514
+ out[k] = REDACTED;
2515
+ } else {
2516
+ out[k] = walk(v, depth + 1);
2517
+ }
2518
+ }
2519
+ return out;
2520
+ }
2521
+ return value;
2522
+ }
2224
2523
  export {
2225
2524
  AuditAlertHandler,
2226
2525
  AuditReportGenerator,
@@ -2248,11 +2547,11 @@ export {
2248
2547
  LogAlertHandler,
2249
2548
  OAuthClient,
2250
2549
  OAuthProviders,
2251
- PasswordHasher,
2252
2550
  PermissionBuilder,
2253
2551
  PermissionCache,
2254
2552
  PolicyBuilder,
2255
2553
  PrivacyPolicyManager,
2554
+ REDACTED,
2256
2555
  RequirePermission,
2257
2556
  RequireRole,
2258
2557
  SecurityAlertService,
@@ -2275,10 +2574,19 @@ export {
2275
2574
  createSecurityMiddleware,
2276
2575
  dataExportSystem,
2277
2576
  encryption,
2577
+ escapeShellArg,
2578
+ escapeSqlIdentifier,
2579
+ isSafeUrl,
2580
+ isSensitiveLogKey,
2278
2581
  permissionCache,
2279
2582
  privacyPolicyManager,
2583
+ redactLogContext,
2584
+ redactLogField,
2585
+ redactSecretsInString,
2586
+ sanitizeHtml,
2587
+ sanitizeTerminalLine,
2588
+ sanitizeUrl,
2280
2589
  setRateLimitHeaders,
2281
2590
  signAuditEntry,
2282
2591
  verifyAuditEntry
2283
2592
  };
2284
- //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,18 +1,19 @@
1
1
  {
2
2
  "name": "@revealui/security",
3
- "version": "0.2.6",
3
+ "version": "0.3.0",
4
4
  "description": "Security infrastructure for RevealUI - headers, CORS, RBAC/ABAC, encryption, audit, GDPR",
5
5
  "license": "MIT",
6
6
  "dependencies": {
7
- "@revealui/contracts": "1.3.6",
8
- "@revealui/utils": "0.3.3"
7
+ "parse5": "^8.0.0",
8
+ "@revealui/contracts": "1.4.0",
9
+ "@revealui/utils": "0.3.4"
9
10
  },
10
11
  "devDependencies": {
11
- "@types/node": "^25.5.0",
12
+ "@types/node": "^25.5.2",
12
13
  "tsup": "^8.5.1",
13
14
  "typescript": "^6.0.2",
14
- "vitest": "^4.1.0",
15
- "dev": "0.0.1"
15
+ "vitest": "^4.1.3",
16
+ "@revealui/dev": "0.1.0"
16
17
  },
17
18
  "engines": {
18
19
  "node": ">=24.13.0"
@@ -38,6 +39,20 @@
38
39
  "url": "https://github.com/RevealUIStudio/revealui.git",
39
40
  "directory": "packages/security"
40
41
  },
42
+ "homepage": "https://revealui.com",
43
+ "author": "RevealUI Studio <founder@revealui.com>",
44
+ "bugs": {
45
+ "url": "https://github.com/RevealUIStudio/revealui/issues"
46
+ },
47
+ "keywords": [
48
+ "revealui",
49
+ "security",
50
+ "cors",
51
+ "csp",
52
+ "rbac",
53
+ "encryption",
54
+ "gdpr"
55
+ ],
41
56
  "scripts": {
42
57
  "build": "tsup",
43
58
  "clean": "rm -rf dist",
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/audit.ts"],"sourcesContent":["/**\n * Audit Logging System\n *\n * Track security-relevant events and user actions for compliance\n */\n\nexport type AuditEventType =\n | 'auth.login'\n | 'auth.logout'\n | 'auth.failed_login'\n | 'auth.password_change'\n | 'auth.password_reset'\n | 'auth.mfa_enabled'\n | 'auth.mfa_disabled'\n | 'user.create'\n | 'user.update'\n | 'user.delete'\n | 'user.view'\n | 'data.create'\n | 'data.read'\n | 'data.update'\n | 'data.delete'\n | 'data.export'\n | 'permission.grant'\n | 'permission.revoke'\n | 'role.assign'\n | 'role.remove'\n | 'config.change'\n | 'security.violation'\n | 'security.alert'\n | 'gdpr.consent'\n | 'gdpr.data_request'\n | 'gdpr.data_deletion'\n | `data.${string}`\n | `permission.${string}`\n | `security.${string}`\n | `gdpr.${string}`;\n\nexport type AuditSeverity = 'low' | 'medium' | 'high' | 'critical';\n\nexport interface AuditEvent {\n id: string;\n timestamp: string;\n type: AuditEventType;\n severity: AuditSeverity;\n actor: {\n id: string;\n type: 'user' | 'system' | 'api';\n ip?: string;\n userAgent?: string;\n };\n resource?: {\n type: string;\n id: string;\n name?: string;\n };\n action: string;\n result: 'success' | 'failure' | 'partial';\n changes?: {\n before?: Record<string, unknown>;\n after?: Record<string, unknown>;\n };\n metadata?: Record<string, unknown>;\n message?: string;\n}\n\nexport interface AuditQuery {\n types?: AuditEventType[];\n actorId?: string;\n resourceType?: string;\n resourceId?: string;\n startDate?: Date;\n endDate?: Date;\n severity?: AuditSeverity[];\n result?: ('success' | 'failure' | 'partial')[];\n limit?: number;\n offset?: number;\n}\n\nexport interface AuditStorage {\n write(event: AuditEvent): Promise<void>;\n query(query: AuditQuery): Promise<AuditEvent[]>;\n count(query: AuditQuery): Promise<number>;\n}\n\n/**\n * Audit logging system\n */\nexport class AuditSystem {\n private storage: AuditStorage;\n private filters: Array<(event: AuditEvent) => boolean> = [];\n\n constructor(storage: AuditStorage) {\n this.storage = storage;\n }\n\n /**\n * Replace the backing storage (e.g. swap InMemory for Postgres at startup).\n * Events already written to the old storage are NOT migrated.\n */\n setStorage(storage: AuditStorage): void {\n this.storage = storage;\n }\n\n /**\n * Log audit event\n */\n async log(event: Omit<AuditEvent, 'id' | 'timestamp'>): Promise<void> {\n const fullEvent: AuditEvent = {\n ...event,\n id: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n };\n\n // Apply filters\n const shouldLog = this.filters.every((filter) => filter(fullEvent));\n\n if (!shouldLog) {\n return;\n }\n\n await this.storage.write(fullEvent);\n }\n\n /**\n * Log authentication event\n */\n async logAuth(\n type: Extract<\n AuditEventType,\n 'auth.login' | 'auth.logout' | 'auth.failed_login' | 'auth.password_change'\n >,\n actorId: string,\n result: 'success' | 'failure',\n metadata?: Record<string, unknown>,\n ): Promise<void> {\n await this.log({\n type,\n severity: result === 'failure' ? 'medium' : 'low',\n actor: {\n id: actorId,\n type: 'user',\n },\n action: (type as string).replace('auth.', ''),\n result,\n metadata,\n });\n }\n\n /**\n * Log data access event\n */\n async logDataAccess(\n action: 'create' | 'read' | 'update' | 'delete',\n actorId: string,\n resourceType: string,\n resourceId: string,\n result: 'success' | 'failure',\n changes?: { before?: Record<string, unknown>; after?: Record<string, unknown> },\n ): Promise<void> {\n await this.log({\n type: `data.${action}` as AuditEventType,\n severity: action === 'delete' ? 'high' : 'medium',\n actor: {\n id: actorId,\n type: 'user',\n },\n resource: {\n type: resourceType,\n id: resourceId,\n },\n action,\n result,\n changes,\n });\n }\n\n /**\n * Log permission change\n */\n async logPermissionChange(\n action: 'grant' | 'revoke',\n actorId: string,\n targetUserId: string,\n permission: string,\n result: 'success' | 'failure',\n ): Promise<void> {\n await this.log({\n type: `permission.${action}` as AuditEventType,\n severity: 'high',\n actor: {\n id: actorId,\n type: 'user',\n },\n resource: {\n type: 'user',\n id: targetUserId,\n },\n action,\n result,\n metadata: {\n permission,\n },\n });\n }\n\n /**\n * Log security event\n */\n async logSecurityEvent(\n type: 'violation' | 'alert',\n severity: AuditSeverity,\n actorId: string,\n message: string,\n metadata?: Record<string, unknown>,\n ): Promise<void> {\n await this.log({\n type: `security.${type}` as AuditEventType,\n severity,\n actor: {\n id: actorId,\n type: 'user',\n },\n action: type,\n result: 'failure',\n message,\n metadata,\n });\n }\n\n /**\n * Log GDPR event\n */\n async logGDPREvent(\n type: 'consent' | 'data_request' | 'data_deletion',\n actorId: string,\n result: 'success' | 'failure',\n metadata?: Record<string, unknown>,\n ): Promise<void> {\n await this.log({\n type: `gdpr.${type}` as AuditEventType,\n severity: 'high',\n actor: {\n id: actorId,\n type: 'user',\n },\n action: type,\n result,\n metadata,\n });\n }\n\n /**\n * Query audit logs\n */\n async query(query: AuditQuery): Promise<AuditEvent[]> {\n return this.storage.query(query);\n }\n\n /**\n * Count audit logs\n */\n async count(query: AuditQuery): Promise<number> {\n return this.storage.count(query);\n }\n\n /**\n * Add filter\n */\n addFilter(filter: (event: AuditEvent) => boolean): void {\n this.filters.push(filter);\n }\n\n /**\n * Remove filter\n */\n removeFilter(filter: (event: AuditEvent) => boolean): void {\n const index = this.filters.indexOf(filter);\n if (index > -1) {\n this.filters.splice(index, 1);\n }\n }\n}\n\n/**\n * In-memory audit storage (for development)\n */\nexport class InMemoryAuditStorage implements AuditStorage {\n private events: AuditEvent[] = [];\n private maxEvents: number;\n\n constructor(maxEvents: number = 10000) {\n this.maxEvents = maxEvents;\n }\n\n async write(event: AuditEvent): Promise<void> {\n this.events.push(event);\n\n // Trim old events\n if (this.events.length > this.maxEvents) {\n this.events.shift();\n }\n }\n\n async query(query: AuditQuery): Promise<AuditEvent[]> {\n let results = [...this.events];\n\n // Filter by type\n if (query.types && query.types.length > 0) {\n results = results.filter((e) => query.types?.includes(e.type));\n }\n\n // Filter by actor\n if (query.actorId) {\n results = results.filter((e) => e.actor.id === query.actorId);\n }\n\n // Filter by resource\n if (query.resourceType) {\n results = results.filter((e) => e.resource?.type === query.resourceType);\n }\n\n if (query.resourceId) {\n results = results.filter((e) => e.resource?.id === query.resourceId);\n }\n\n // Filter by date range\n if (query.startDate) {\n const startDate = query.startDate;\n results = results.filter((e) => new Date(e.timestamp) >= startDate);\n }\n\n if (query.endDate) {\n const endDate = query.endDate;\n results = results.filter((e) => new Date(e.timestamp) <= endDate);\n }\n\n // Filter by severity\n if (query.severity && query.severity.length > 0) {\n results = results.filter((e) => query.severity?.includes(e.severity));\n }\n\n // Filter by result\n if (query.result && query.result.length > 0) {\n results = results.filter((e) => query.result?.includes(e.result));\n }\n\n // Sort by timestamp (newest first)\n results.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());\n\n // Apply pagination\n const offset = query.offset || 0;\n const limit = query.limit || 100;\n\n return results.slice(offset, offset + limit);\n }\n\n async count(query: AuditQuery): Promise<number> {\n const results = await this.query({ ...query, limit: undefined, offset: undefined });\n return results.length;\n }\n\n /**\n * Clear all events\n */\n clear(): void {\n this.events = [];\n }\n\n /**\n * Get all events\n */\n getAll(): AuditEvent[] {\n return [...this.events];\n }\n}\n\n/**\n * Audit trail decorator\n */\nexport function AuditTrail(\n type: AuditEventType,\n action: string,\n options?: {\n severity?: AuditSeverity;\n captureChanges?: boolean;\n resourceType?: string;\n },\n) {\n return (_target: object, _propertyKey: string, descriptor: PropertyDescriptor) => {\n const originalMethod = descriptor.value;\n\n descriptor.value = async function (\n this: { user?: { id?: string }; audit?: AuditSystem },\n ...args: unknown[]\n ) {\n const actorId = this.user?.id || 'system';\n const before = options?.captureChanges ? args[0] : undefined;\n\n let result: 'success' | 'failure' | 'partial' = 'success';\n let error: Error | undefined;\n\n try {\n const returnValue = await originalMethod.apply(this, args);\n\n // Log audit event\n if (this.audit) {\n await this.audit.log({\n type,\n severity: options?.severity || 'medium',\n actor: {\n id: actorId,\n type: 'user',\n },\n resource: options?.resourceType\n ? {\n type: options.resourceType,\n id: (args[0] as { id?: string })?.id || 'unknown',\n }\n : undefined,\n action,\n result,\n changes: options?.captureChanges\n ? {\n before: before as Record<string, unknown> | undefined,\n after: returnValue as Record<string, unknown> | undefined,\n }\n : undefined,\n });\n }\n\n return returnValue;\n } catch (err) {\n result = 'failure';\n error = err as Error;\n\n // Log failure\n if (this.audit) {\n await this.audit.log({\n type,\n severity: 'high',\n actor: {\n id: actorId,\n type: 'user',\n },\n resource: options?.resourceType\n ? {\n type: options.resourceType,\n id: (args[0] as { id?: string })?.id || 'unknown',\n }\n : undefined,\n action,\n result,\n message: error.message,\n });\n }\n\n throw error;\n }\n };\n\n return descriptor;\n };\n}\n\n/**\n * Audit middleware\n */\nexport function createAuditMiddleware<TRequest = unknown, TResponse = unknown>(\n audit: AuditSystem,\n getUser: (request: TRequest) => { id: string; ip?: string; userAgent?: string },\n) {\n return async (\n request: TRequest & { method: string; url: string },\n next: () => Promise<TResponse & { status?: number }>,\n ) => {\n const user = getUser(request);\n const startTime = Date.now();\n\n try {\n const response = await next();\n\n // Log successful request\n await audit.log({\n type: 'data.read',\n severity: 'low',\n actor: {\n id: user.id,\n type: 'user',\n ip: user.ip,\n userAgent: user.userAgent,\n },\n action: request.method,\n result: 'success',\n metadata: {\n path: request.url,\n duration: Date.now() - startTime,\n status: response.status,\n },\n });\n\n return response;\n } catch (error) {\n // Log failed request\n await audit.log({\n type: 'data.read',\n severity: 'medium',\n actor: {\n id: user.id,\n type: 'user',\n ip: user.ip,\n userAgent: user.userAgent,\n },\n action: request.method,\n result: 'failure',\n message: error instanceof Error ? error.message : 'Unknown error',\n metadata: {\n path: request.url,\n duration: Date.now() - startTime,\n },\n });\n\n throw error;\n }\n };\n}\n\n/**\n * Audit report generator\n */\nexport class AuditReportGenerator {\n constructor(private audit: AuditSystem) {}\n\n /**\n * Generate security report\n */\n async generateSecurityReport(\n startDate: Date,\n endDate: Date,\n ): Promise<{\n totalEvents: number;\n securityViolations: number;\n failedLogins: number;\n permissionChanges: number;\n dataExports: number;\n criticalEvents: AuditEvent[];\n }> {\n const allEvents = await this.audit.query({\n startDate,\n endDate,\n });\n\n const securityViolations = allEvents.filter((e) => e.type.startsWith('security.')).length;\n\n const failedLogins = allEvents.filter((e) => e.type === 'auth.failed_login').length;\n\n const permissionChanges = allEvents.filter((e) => e.type.startsWith('permission.')).length;\n\n const dataExports = allEvents.filter((e) => e.type === 'data.export').length;\n\n const criticalEvents = allEvents.filter((e) => e.severity === 'critical');\n\n return {\n totalEvents: allEvents.length,\n securityViolations,\n failedLogins,\n permissionChanges,\n dataExports,\n criticalEvents,\n };\n }\n\n /**\n * Generate user activity report\n */\n async generateUserActivityReport(\n userId: string,\n startDate: Date,\n endDate: Date,\n ): Promise<{\n totalActions: number;\n actionsByType: Record<string, number>;\n failedActions: number;\n recentActions: AuditEvent[];\n }> {\n const events = await this.audit.query({\n actorId: userId,\n startDate,\n endDate,\n });\n\n const actionsByType = events.reduce(\n (acc, event) => {\n acc[event.type] = (acc[event.type] || 0) + 1;\n return acc;\n },\n {} as Record<string, number>,\n );\n\n const failedActions = events.filter((e) => e.result === 'failure').length;\n\n return {\n totalActions: events.length,\n actionsByType,\n failedActions,\n recentActions: events.slice(0, 10),\n };\n }\n\n /**\n * Generate compliance report\n */\n async generateComplianceReport(\n startDate: Date,\n endDate: Date,\n ): Promise<{\n dataAccesses: number;\n dataModifications: number;\n dataDeletions: number;\n gdprRequests: number;\n auditTrailComplete: boolean;\n }> {\n const events = await this.audit.query({\n startDate,\n endDate,\n });\n\n const dataAccesses = events.filter((e) => e.type === 'data.read').length;\n\n const dataModifications = events.filter(\n (e) => e.type === 'data.update' || e.type === 'data.create',\n ).length;\n\n const dataDeletions = events.filter((e) => e.type === 'data.delete').length;\n\n const gdprRequests = events.filter((e) => e.type.startsWith('gdpr.')).length;\n\n // Check if audit trail is complete (no gaps)\n const auditTrailComplete = this.checkAuditTrailContinuity(events);\n\n return {\n dataAccesses,\n dataModifications,\n dataDeletions,\n gdprRequests,\n auditTrailComplete,\n };\n }\n\n /**\n * Check audit trail continuity\n */\n private checkAuditTrailContinuity(events: AuditEvent[]): boolean {\n if (events.length === 0) return true;\n\n // Sort by timestamp\n const sorted = events.sort(\n (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime(),\n );\n\n // Check for gaps (simplified - just check if we have events)\n return sorted.length > 0;\n }\n}\n\n// =============================================================================\n// Audit Log Integrity — HMAC-SHA256 Signing\n// =============================================================================\n\n/** Fields included in the HMAC signature for tamper detection. */\ninterface SignableFields {\n timestamp: string;\n eventType: string;\n severity: string;\n agentId: string;\n payload: unknown;\n}\n\n/**\n * Compute an HMAC-SHA256 signature over the canonical fields of an audit entry.\n *\n * The signature covers `timestamp`, `eventType`, `severity`, `agentId`, and\n * `payload` — the immutable core of every audit record. Changing any of\n * these fields after signing will cause verification to fail.\n *\n * @param entry - The audit entry fields to sign\n * @param secret - The HMAC secret key\n * @returns Hex-encoded HMAC-SHA256 signature\n */\nexport async function signAuditEntry(entry: SignableFields, secret: string): Promise<string> {\n const { createHmac } = await import('node:crypto');\n const canonical = JSON.stringify({\n timestamp: entry.timestamp,\n eventType: entry.eventType,\n severity: entry.severity,\n agentId: entry.agentId,\n payload: entry.payload,\n });\n return createHmac('sha256', secret).update(canonical).digest('hex');\n}\n\n/**\n * Verify an HMAC-SHA256 signature against the canonical fields of an audit entry.\n *\n * Uses timing-safe comparison to prevent timing attacks.\n *\n * @param entry - The audit entry fields to verify\n * @param signature - The hex-encoded HMAC-SHA256 signature to verify\n * @param secret - The HMAC secret key\n * @returns True if the signature is valid\n */\nexport async function verifyAuditEntry(\n entry: SignableFields,\n signature: string,\n secret: string,\n): Promise<boolean> {\n const { timingSafeEqual } = await import('node:crypto');\n const expected = await signAuditEntry(entry, secret);\n\n // Lengths must match for timingSafeEqual\n if (expected.length !== signature.length) {\n return false;\n }\n\n return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));\n}\n\n/**\n * Global audit system\n */\nexport const audit = new AuditSystem(new InMemoryAuditStorage());\n"],"mappings":";AAwFO,IAAM,cAAN,MAAkB;AAAA,EACf;AAAA,EACA,UAAiD,CAAC;AAAA,EAE1D,YAAY,SAAuB;AACjC,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,WAAW,SAA6B;AACtC,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAI,OAA4D;AACpE,UAAM,YAAwB;AAAA,MAC5B,GAAG;AAAA,MACH,IAAI,OAAO,WAAW;AAAA,MACtB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC;AAGA,UAAM,YAAY,KAAK,QAAQ,MAAM,CAAC,WAAW,OAAO,SAAS,CAAC;AAElE,QAAI,CAAC,WAAW;AACd;AAAA,IACF;AAEA,UAAM,KAAK,QAAQ,MAAM,SAAS;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QACJ,MAIA,SACA,QACA,UACe;AACf,UAAM,KAAK,IAAI;AAAA,MACb;AAAA,MACA,UAAU,WAAW,YAAY,WAAW;AAAA,MAC5C,OAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM;AAAA,MACR;AAAA,MACA,QAAS,KAAgB,QAAQ,SAAS,EAAE;AAAA,MAC5C;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cACJ,QACA,SACA,cACA,YACA,QACA,SACe;AACf,UAAM,KAAK,IAAI;AAAA,MACb,MAAM,QAAQ,MAAM;AAAA,MACpB,UAAU,WAAW,WAAW,SAAS;AAAA,MACzC,OAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM;AAAA,MACR;AAAA,MACA,UAAU;AAAA,QACR,MAAM;AAAA,QACN,IAAI;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBACJ,QACA,SACA,cACA,YACA,QACe;AACf,UAAM,KAAK,IAAI;AAAA,MACb,MAAM,cAAc,MAAM;AAAA,MAC1B,UAAU;AAAA,MACV,OAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM;AAAA,MACR;AAAA,MACA,UAAU;AAAA,QACR,MAAM;AAAA,QACN,IAAI;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA,UAAU;AAAA,QACR;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBACJ,MACA,UACA,SACA,SACA,UACe;AACf,UAAM,KAAK,IAAI;AAAA,MACb,MAAM,YAAY,IAAI;AAAA,MACtB;AAAA,MACA,OAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM;AAAA,MACR;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,aACJ,MACA,SACA,QACA,UACe;AACf,UAAM,KAAK,IAAI;AAAA,MACb,MAAM,QAAQ,IAAI;AAAA,MAClB,UAAU;AAAA,MACV,OAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM;AAAA,MACR;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,OAA0C;AACpD,WAAO,KAAK,QAAQ,MAAM,KAAK;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,OAAoC;AAC9C,WAAO,KAAK,QAAQ,MAAM,KAAK;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,QAA8C;AACtD,SAAK,QAAQ,KAAK,MAAM;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,QAA8C;AACzD,UAAM,QAAQ,KAAK,QAAQ,QAAQ,MAAM;AACzC,QAAI,QAAQ,IAAI;AACd,WAAK,QAAQ,OAAO,OAAO,CAAC;AAAA,IAC9B;AAAA,EACF;AACF;AAKO,IAAM,uBAAN,MAAmD;AAAA,EAChD,SAAuB,CAAC;AAAA,EACxB;AAAA,EAER,YAAY,YAAoB,KAAO;AACrC,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,MAAM,OAAkC;AAC5C,SAAK,OAAO,KAAK,KAAK;AAGtB,QAAI,KAAK,OAAO,SAAS,KAAK,WAAW;AACvC,WAAK,OAAO,MAAM;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,MAAM,MAAM,OAA0C;AACpD,QAAI,UAAU,CAAC,GAAG,KAAK,MAAM;AAG7B,QAAI,MAAM,SAAS,MAAM,MAAM,SAAS,GAAG;AACzC,gBAAU,QAAQ,OAAO,CAAC,MAAM,MAAM,OAAO,SAAS,EAAE,IAAI,CAAC;AAAA,IAC/D;AAGA,QAAI,MAAM,SAAS;AACjB,gBAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,MAAM,OAAO,MAAM,OAAO;AAAA,IAC9D;AAGA,QAAI,MAAM,cAAc;AACtB,gBAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,UAAU,SAAS,MAAM,YAAY;AAAA,IACzE;AAEA,QAAI,MAAM,YAAY;AACpB,gBAAU,QAAQ,OAAO,CAAC,MAAM,EAAE,UAAU,OAAO,MAAM,UAAU;AAAA,IACrE;AAGA,QAAI,MAAM,WAAW;AACnB,YAAM,YAAY,MAAM;AACxB,gBAAU,QAAQ,OAAO,CAAC,MAAM,IAAI,KAAK,EAAE,SAAS,KAAK,SAAS;AAAA,IACpE;AAEA,QAAI,MAAM,SAAS;AACjB,YAAM,UAAU,MAAM;AACtB,gBAAU,QAAQ,OAAO,CAAC,MAAM,IAAI,KAAK,EAAE,SAAS,KAAK,OAAO;AAAA,IAClE;AAGA,QAAI,MAAM,YAAY,MAAM,SAAS,SAAS,GAAG;AAC/C,gBAAU,QAAQ,OAAO,CAAC,MAAM,MAAM,UAAU,SAAS,EAAE,QAAQ,CAAC;AAAA,IACtE;AAGA,QAAI,MAAM,UAAU,MAAM,OAAO,SAAS,GAAG;AAC3C,gBAAU,QAAQ,OAAO,CAAC,MAAM,MAAM,QAAQ,SAAS,EAAE,MAAM,CAAC;AAAA,IAClE;AAGA,YAAQ,KAAK,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,IAAI,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC;AAGxF,UAAM,SAAS,MAAM,UAAU;AAC/B,UAAM,QAAQ,MAAM,SAAS;AAE7B,WAAO,QAAQ,MAAM,QAAQ,SAAS,KAAK;AAAA,EAC7C;AAAA,EAEA,MAAM,MAAM,OAAoC;AAC9C,UAAM,UAAU,MAAM,KAAK,MAAM,EAAE,GAAG,OAAO,OAAO,QAAW,QAAQ,OAAU,CAAC;AAClF,WAAO,QAAQ;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACZ,SAAK,SAAS,CAAC;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAKA,SAAuB;AACrB,WAAO,CAAC,GAAG,KAAK,MAAM;AAAA,EACxB;AACF;AAKO,SAAS,WACd,MACA,QACA,SAKA;AACA,SAAO,CAAC,SAAiB,cAAsB,eAAmC;AAChF,UAAM,iBAAiB,WAAW;AAElC,eAAW,QAAQ,kBAEd,MACH;AACA,YAAM,UAAU,KAAK,MAAM,MAAM;AACjC,YAAM,SAAS,SAAS,iBAAiB,KAAK,CAAC,IAAI;AAEnD,UAAI,SAA4C;AAChD,UAAI;AAEJ,UAAI;AACF,cAAM,cAAc,MAAM,eAAe,MAAM,MAAM,IAAI;AAGzD,YAAI,KAAK,OAAO;AACd,gBAAM,KAAK,MAAM,IAAI;AAAA,YACnB;AAAA,YACA,UAAU,SAAS,YAAY;AAAA,YAC/B,OAAO;AAAA,cACL,IAAI;AAAA,cACJ,MAAM;AAAA,YACR;AAAA,YACA,UAAU,SAAS,eACf;AAAA,cACE,MAAM,QAAQ;AAAA,cACd,IAAK,KAAK,CAAC,GAAuB,MAAM;AAAA,YAC1C,IACA;AAAA,YACJ;AAAA,YACA;AAAA,YACA,SAAS,SAAS,iBACd;AAAA,cACE;AAAA,cACA,OAAO;AAAA,YACT,IACA;AAAA,UACN,CAAC;AAAA,QACH;AAEA,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,iBAAS;AACT,gBAAQ;AAGR,YAAI,KAAK,OAAO;AACd,gBAAM,KAAK,MAAM,IAAI;AAAA,YACnB;AAAA,YACA,UAAU;AAAA,YACV,OAAO;AAAA,cACL,IAAI;AAAA,cACJ,MAAM;AAAA,YACR;AAAA,YACA,UAAU,SAAS,eACf;AAAA,cACE,MAAM,QAAQ;AAAA,cACd,IAAK,KAAK,CAAC,GAAuB,MAAM;AAAA,YAC1C,IACA;AAAA,YACJ;AAAA,YACA;AAAA,YACA,SAAS,MAAM;AAAA,UACjB,CAAC;AAAA,QACH;AAEA,cAAM;AAAA,MACR;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;AAKO,SAAS,sBACdA,QACA,SACA;AACA,SAAO,OACL,SACA,SACG;AACH,UAAM,OAAO,QAAQ,OAAO;AAC5B,UAAM,YAAY,KAAK,IAAI;AAE3B,QAAI;AACF,YAAM,WAAW,MAAM,KAAK;AAG5B,YAAMA,OAAM,IAAI;AAAA,QACd,MAAM;AAAA,QACN,UAAU;AAAA,QACV,OAAO;AAAA,UACL,IAAI,KAAK;AAAA,UACT,MAAM;AAAA,UACN,IAAI,KAAK;AAAA,UACT,WAAW,KAAK;AAAA,QAClB;AAAA,QACA,QAAQ,QAAQ;AAAA,QAChB,QAAQ;AAAA,QACR,UAAU;AAAA,UACR,MAAM,QAAQ;AAAA,UACd,UAAU,KAAK,IAAI,IAAI;AAAA,UACvB,QAAQ,SAAS;AAAA,QACnB;AAAA,MACF,CAAC;AAED,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,YAAMA,OAAM,IAAI;AAAA,QACd,MAAM;AAAA,QACN,UAAU;AAAA,QACV,OAAO;AAAA,UACL,IAAI,KAAK;AAAA,UACT,MAAM;AAAA,UACN,IAAI,KAAK;AAAA,UACT,WAAW,KAAK;AAAA,QAClB;AAAA,QACA,QAAQ,QAAQ;AAAA,QAChB,QAAQ;AAAA,QACR,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QAClD,UAAU;AAAA,UACR,MAAM,QAAQ;AAAA,UACd,UAAU,KAAK,IAAI,IAAI;AAAA,QACzB;AAAA,MACF,CAAC;AAED,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAKO,IAAM,uBAAN,MAA2B;AAAA,EAChC,YAAoBA,QAAoB;AAApB,iBAAAA;AAAA,EAAqB;AAAA;AAAA;AAAA;AAAA,EAKzC,MAAM,uBACJ,WACA,SAQC;AACD,UAAM,YAAY,MAAM,KAAK,MAAM,MAAM;AAAA,MACvC;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,qBAAqB,UAAU,OAAO,CAAC,MAAM,EAAE,KAAK,WAAW,WAAW,CAAC,EAAE;AAEnF,UAAM,eAAe,UAAU,OAAO,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE;AAE7E,UAAM,oBAAoB,UAAU,OAAO,CAAC,MAAM,EAAE,KAAK,WAAW,aAAa,CAAC,EAAE;AAEpF,UAAM,cAAc,UAAU,OAAO,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE;AAEtE,UAAM,iBAAiB,UAAU,OAAO,CAAC,MAAM,EAAE,aAAa,UAAU;AAExE,WAAO;AAAA,MACL,aAAa,UAAU;AAAA,MACvB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,2BACJ,QACA,WACA,SAMC;AACD,UAAM,SAAS,MAAM,KAAK,MAAM,MAAM;AAAA,MACpC,SAAS;AAAA,MACT;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,gBAAgB,OAAO;AAAA,MAC3B,CAAC,KAAK,UAAU;AACd,YAAI,MAAM,IAAI,KAAK,IAAI,MAAM,IAAI,KAAK,KAAK;AAC3C,eAAO;AAAA,MACT;AAAA,MACA,CAAC;AAAA,IACH;AAEA,UAAM,gBAAgB,OAAO,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE;AAEnE,WAAO;AAAA,MACL,cAAc,OAAO;AAAA,MACrB;AAAA,MACA;AAAA,MACA,eAAe,OAAO,MAAM,GAAG,EAAE;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,yBACJ,WACA,SAOC;AACD,UAAM,SAAS,MAAM,KAAK,MAAM,MAAM;AAAA,MACpC;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,eAAe,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE;AAElE,UAAM,oBAAoB,OAAO;AAAA,MAC/B,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,SAAS;AAAA,IAChD,EAAE;AAEF,UAAM,gBAAgB,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE;AAErE,UAAM,eAAe,OAAO,OAAO,CAAC,MAAM,EAAE,KAAK,WAAW,OAAO,CAAC,EAAE;AAGtE,UAAM,qBAAqB,KAAK,0BAA0B,MAAM;AAEhE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,0BAA0B,QAA+B;AAC/D,QAAI,OAAO,WAAW,EAAG,QAAO;AAGhC,UAAM,SAAS,OAAO;AAAA,MACpB,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,IAAI,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ;AAAA,IAC5E;AAGA,WAAO,OAAO,SAAS;AAAA,EACzB;AACF;AA0BA,eAAsB,eAAe,OAAuB,QAAiC;AAC3F,QAAM,EAAE,WAAW,IAAI,MAAM,OAAO,QAAa;AACjD,QAAM,YAAY,KAAK,UAAU;AAAA,IAC/B,WAAW,MAAM;AAAA,IACjB,WAAW,MAAM;AAAA,IACjB,UAAU,MAAM;AAAA,IAChB,SAAS,MAAM;AAAA,IACf,SAAS,MAAM;AAAA,EACjB,CAAC;AACD,SAAO,WAAW,UAAU,MAAM,EAAE,OAAO,SAAS,EAAE,OAAO,KAAK;AACpE;AAYA,eAAsB,iBACpB,OACA,WACA,QACkB;AAClB,QAAM,EAAE,gBAAgB,IAAI,MAAM,OAAO,QAAa;AACtD,QAAM,WAAW,MAAM,eAAe,OAAO,MAAM;AAGnD,MAAI,SAAS,WAAW,UAAU,QAAQ;AACxC,WAAO;AAAA,EACT;AAEA,SAAO,gBAAgB,OAAO,KAAK,QAAQ,GAAG,OAAO,KAAK,SAAS,CAAC;AACtE;AAKO,IAAM,QAAQ,IAAI,YAAY,IAAI,qBAAqB,CAAC;","names":["audit"]}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/logger.ts","../src/alerting.ts","../src/auth.ts","../src/authorization.ts","../src/encryption.ts","../src/gdpr.ts","../src/gdpr-storage.ts","../src/headers.ts"],"sourcesContent":["/**\n * Internal logger for @revealui/security.\n *\n * Defaults to `console`. Consumers should call `configureSecurityLogger()`\n * to supply a structured logger (e.g. from `@revealui/utils/logger`).\n */\n\nexport interface SecurityLogger {\n warn(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n info(message: string, ...args: unknown[]): void;\n debug(message: string, ...args: unknown[]): void;\n}\n\nlet securityLogger: SecurityLogger = console;\n\n/**\n * Replace the default console logger with a structured logger.\n */\nexport function configureSecurityLogger(logger: SecurityLogger): void {\n securityLogger = logger;\n}\n\n/**\n * Get the current security logger instance.\n */\nexport function getSecurityLogger(): SecurityLogger {\n return securityLogger;\n}\n","/**\n * Security Alerting Service\n *\n * Evaluates audit events against configurable threshold rules and\n * dispatches alerts through pluggable handlers (logging, audit trail,\n * webhook / SIEM integration).\n */\n\nimport type { AuditEvent, AuditSeverity } from './audit.js';\nimport { getSecurityLogger } from './logger.js';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** A security alert produced when a threshold is breached. */\nexport interface SecurityAlert {\n /** Alert rule that triggered (e.g. 'failedLogins', 'accountLockout'). */\n type: string;\n /** Severity of the alert. */\n severity: AuditSeverity;\n /** Human-readable description. */\n message: string;\n /** Contextual data attached to the alert. */\n context: Record<string, unknown>;\n /** When the alert was raised (ISO-8601). */\n timestamp: string;\n}\n\n/** Handler that receives dispatched security alerts. */\nexport interface AlertHandler {\n /** Process a single alert. */\n handle(alert: SecurityAlert): Promise<void>;\n}\n\n/** Configuration for a single threshold rule. */\nexport interface ThresholdRule {\n /** Maximum event count before an alert fires. */\n maxCount: number;\n /** Sliding window duration in milliseconds. */\n windowMs: number;\n /** Severity assigned to alerts from this rule. */\n severity: AuditSeverity;\n /** Human-readable message template — `{count}` is replaced at runtime. */\n messageTemplate: string;\n}\n\n/** Top-level configuration for the alerting service. */\nexport interface AlertingConfig {\n /** Threshold rules keyed by rule name. */\n thresholds: Record<string, ThresholdRule>;\n /** Handlers that receive dispatched alerts. */\n handlers: AlertHandler[];\n}\n\n// =============================================================================\n// Built-in threshold rules\n// =============================================================================\n\n/** Default threshold rules aligned with SOC2 6.2 requirements. */\nexport const DEFAULT_THRESHOLDS: Record<string, ThresholdRule> = {\n failedLogins: {\n maxCount: 10,\n windowMs: 15 * 60 * 1000,\n severity: 'high',\n messageTemplate: 'Excessive failed logins detected: {count} attempts in 15 minutes',\n },\n privilegeEscalation: {\n maxCount: 1,\n windowMs: 60 * 60 * 1000,\n severity: 'critical',\n messageTemplate: 'Privilege escalation detected: role changed to admin',\n },\n massDataExport: {\n maxCount: 100,\n windowMs: 60 * 60 * 1000,\n severity: 'high',\n messageTemplate: 'Mass data export detected: {count} exports in 1 hour',\n },\n accountLockout: {\n maxCount: 1,\n windowMs: 60 * 60 * 1000,\n severity: 'high',\n messageTemplate: 'Account lockout triggered',\n },\n mfaDisabled: {\n maxCount: 1,\n windowMs: 60 * 60 * 1000,\n severity: 'critical',\n messageTemplate: 'MFA disabled on account',\n },\n};\n\n// =============================================================================\n// Built-in alert handlers\n// =============================================================================\n\n/**\n * Logs alerts to the structured security logger.\n */\nexport class LogAlertHandler implements AlertHandler {\n /** Write alert details to the configured security logger. */\n async handle(alert: SecurityAlert): Promise<void> {\n const logger = getSecurityLogger();\n const prefix = `[SecurityAlert:${alert.type}]`;\n\n if (alert.severity === 'critical') {\n logger.error(`${prefix} ${alert.message}`, alert.context);\n } else {\n logger.warn(`${prefix} ${alert.message}`, alert.context);\n }\n }\n}\n\n/**\n * Writes alerts as critical audit events into the audit log.\n */\nexport class AuditAlertHandler implements AlertHandler {\n /** Record the alert in the audit trail with severity 'critical'. */\n async handle(alert: SecurityAlert): Promise<void> {\n try {\n const { audit } = await import('./audit.js');\n await audit.logSecurityEvent(\n 'alert',\n 'critical',\n (alert.context.actorId as string) ?? 'system',\n alert.message,\n { alertType: alert.type, ...alert.context },\n );\n } catch {\n // If audit system is unavailable, silently skip\n }\n }\n}\n\n/**\n * POSTs alerts to a configurable webhook URL for SIEM integration.\n */\nexport class WebhookAlertHandler implements AlertHandler {\n private url: string;\n private headers: Record<string, string>;\n\n /**\n * Create a webhook alert handler.\n *\n * @param url - The webhook endpoint URL\n * @param headers - Additional HTTP headers (e.g. authorization)\n */\n constructor(url: string, headers: Record<string, string> = {}) {\n this.url = url;\n this.headers = headers;\n }\n\n /** POST the alert payload to the configured webhook URL. */\n async handle(alert: SecurityAlert): Promise<void> {\n try {\n await fetch(this.url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...this.headers,\n },\n body: JSON.stringify(alert),\n });\n } catch {\n const logger = getSecurityLogger();\n logger.error(`[WebhookAlertHandler] Failed to POST alert to ${this.url}`);\n }\n }\n}\n\n// =============================================================================\n// Event-to-rule mapping\n// =============================================================================\n\n/**\n * Map an audit event to its corresponding threshold rule name.\n * Returns null if the event does not match any known rule.\n */\nfunction mapEventToRule(event: AuditEvent): string | null {\n if (event.type === 'auth.failed_login') {\n return 'failedLogins';\n }\n\n if (event.type === 'role.assign') {\n const newRole = event.changes?.after?.role ?? event.metadata?.role;\n if (newRole === 'admin') {\n return 'privilegeEscalation';\n }\n }\n\n if (event.type === 'data.export') {\n return 'massDataExport';\n }\n\n if (event.action === 'account_locked') {\n return 'accountLockout';\n }\n\n if (event.type === 'auth.mfa_disabled') {\n return 'mfaDisabled';\n }\n\n return null;\n}\n\n/**\n * Derive a grouping key for sliding-window deduplication.\n * Failed logins group by email/actor; exports group globally; others by actor.\n */\nfunction getGroupKey(event: AuditEvent, ruleName: string): string {\n if (ruleName === 'failedLogins') {\n return `${ruleName}:${event.actor.id}`;\n }\n if (ruleName === 'massDataExport') {\n return ruleName;\n }\n return `${ruleName}:${event.actor.id}`;\n}\n\n// =============================================================================\n// SecurityAlertService\n// =============================================================================\n\ninterface WindowEntry {\n timestamps: number[];\n lastAlertAt: number;\n}\n\n/**\n * Evaluates audit events against threshold rules and dispatches alerts.\n *\n * Maintains an in-memory sliding window per rule/group key. When the\n * event count within the window exceeds the threshold, an alert is\n * dispatched to all configured handlers.\n */\nexport class SecurityAlertService {\n private config: AlertingConfig;\n private windows: Map<string, WindowEntry> = new Map();\n\n /**\n * Create a new SecurityAlertService.\n *\n * @param config - Alerting configuration with thresholds and handlers\n */\n constructor(config: AlertingConfig) {\n this.config = config;\n }\n\n /**\n * Evaluate a single audit event against all threshold rules.\n * If a threshold is breached, dispatches alerts to all handlers.\n *\n * @param event - The audit event to evaluate\n * @returns The alert that was dispatched, or null if no threshold was breached\n */\n async evaluateEvent(event: AuditEvent): Promise<SecurityAlert | null> {\n const ruleName = mapEventToRule(event);\n if (!ruleName) {\n return null;\n }\n\n const rule = this.config.thresholds[ruleName];\n if (!rule) {\n return null;\n }\n\n const groupKey = getGroupKey(event, ruleName);\n const now = Date.now();\n const cutoff = now - rule.windowMs;\n\n // Get or create window entry\n let entry = this.windows.get(groupKey);\n if (!entry) {\n entry = { timestamps: [], lastAlertAt: 0 };\n this.windows.set(groupKey, entry);\n }\n\n // Add current event and prune expired entries\n entry.timestamps.push(now);\n entry.timestamps = entry.timestamps.filter((ts) => ts > cutoff);\n\n // Check threshold\n if (entry.timestamps.length < rule.maxCount) {\n return null;\n }\n\n // Prevent duplicate alerts within the same window\n if (entry.lastAlertAt > cutoff) {\n return null;\n }\n\n entry.lastAlertAt = now;\n\n const message = rule.messageTemplate.includes('{count}')\n ? rule.messageTemplate.split('{count}').join(String(entry.timestamps.length))\n : rule.messageTemplate;\n\n const alert: SecurityAlert = {\n type: ruleName,\n severity: rule.severity,\n message,\n context: {\n actorId: event.actor.id,\n eventType: event.type,\n count: entry.timestamps.length,\n windowMs: rule.windowMs,\n },\n timestamp: new Date(now).toISOString(),\n };\n\n // Dispatch to all handlers\n await this.dispatchAlert(alert);\n\n return alert;\n }\n\n /**\n * Clear all sliding window state. Useful for testing.\n */\n reset(): void {\n this.windows.clear();\n }\n\n /**\n * Dispatch an alert to all configured handlers.\n * Errors in individual handlers are logged but do not prevent\n * other handlers from receiving the alert.\n */\n private async dispatchAlert(alert: SecurityAlert): Promise<void> {\n const results = this.config.handlers.map(async (handler) => {\n try {\n await handler.handle(alert);\n } catch {\n const logger = getSecurityLogger();\n logger.error(`[SecurityAlertService] Handler failed for alert type=${alert.type}`);\n }\n });\n await Promise.all(results);\n }\n}\n","/**\n * Authentication Utilities\n *\n * OAuth support, password hashing, and two-factor authentication.\n * JWT-based auth was removed — session auth is handled by @revealui/auth.\n */\n\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\nexport interface User {\n id: string;\n email: string;\n username?: string;\n roles: string[];\n permissions: string[];\n metadata?: Record<string, unknown>;\n}\n\n/**\n * OAuth configuration\n */\nexport interface OAuthConfig {\n provider: 'google' | 'github' | 'microsoft' | 'custom';\n clientId: string;\n clientSecret: string;\n redirectUri: string;\n scope?: string[];\n authorizationUrl?: string;\n tokenUrl?: string;\n userInfoUrl?: string;\n}\n\n/**\n * OAuth provider configurations\n */\nexport const OAuthProviders = {\n google: {\n authorizationUrl: 'https://accounts.google.com/o/oauth2/v2/auth',\n tokenUrl: 'https://oauth2.googleapis.com/token',\n userInfoUrl: 'https://www.googleapis.com/oauth2/v2/userinfo',\n scope: ['openid', 'email', 'profile'],\n },\n github: {\n authorizationUrl: 'https://github.com/login/oauth/authorize',\n tokenUrl: 'https://github.com/login/oauth/access_token',\n userInfoUrl: 'https://api.github.com/user',\n scope: ['user:email'],\n },\n microsoft: {\n authorizationUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',\n tokenUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',\n userInfoUrl: 'https://graph.microsoft.com/v1.0/me',\n scope: ['openid', 'email', 'profile'],\n },\n};\n\n/**\n * OAuth client\n */\nexport class OAuthClient {\n private config: OAuthConfig;\n\n constructor(config: OAuthConfig) {\n // Provider defaults fill in missing fields; user-provided config takes precedence\n this.config = {\n ...OAuthProviders[config.provider as keyof typeof OAuthProviders],\n ...config,\n };\n }\n\n /**\n * Get authorization URL\n */\n getAuthorizationUrl(state?: string): string {\n const params = new URLSearchParams({\n client_id: this.config.clientId,\n redirect_uri: this.config.redirectUri,\n response_type: 'code',\n scope: (this.config.scope || []).join(' '),\n });\n\n if (state) {\n params.append('state', state);\n }\n\n return `${this.config.authorizationUrl}?${params.toString()}`;\n }\n\n /**\n * Exchange code for token\n */\n async exchangeCodeForToken(code: string): Promise<{\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n token_type: string;\n }> {\n if (!this.config.tokenUrl) throw new Error('tokenUrl is required for OAuth');\n const response = await fetch(this.config.tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n client_id: this.config.clientId,\n client_secret: this.config.clientSecret,\n code,\n grant_type: 'authorization_code',\n redirect_uri: this.config.redirectUri,\n }),\n });\n\n if (!response.ok) {\n let detail = '';\n try {\n const body = await response.text();\n detail = `: ${response.status} ${body.slice(0, 200)}`;\n } catch {\n detail = `: ${response.status}`;\n }\n throw new Error(`Failed to exchange code for token${detail}`);\n }\n\n return response.json() as Promise<{\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n token_type: string;\n }>;\n }\n\n /**\n * Get user info\n */\n async getUserInfo(accessToken: string): Promise<{\n id: string;\n email: string;\n name?: string;\n picture?: string;\n }> {\n if (!this.config.userInfoUrl) throw new Error('userInfoUrl is required for OAuth');\n const response = await fetch(this.config.userInfoUrl, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (!response.ok) {\n let detail = '';\n try {\n const body = await response.text();\n detail = `: ${response.status} ${body.slice(0, 200)}`;\n } catch {\n detail = `: ${response.status}`;\n }\n throw new Error(`Failed to fetch user info${detail}`);\n }\n\n return response.json() as Promise<{\n id: string;\n email: string;\n name?: string;\n picture?: string;\n }>;\n }\n}\n\n/**\n * Password hashing utilities\n *\n * Uses PBKDF2 with a random salt for secure password hashing.\n *\n * @deprecated Use `@revealui/auth` instead — it uses bcrypt which is more\n * resistant to GPU brute-force attacks. This PBKDF2 implementation will be\n * removed in a future major version.\n */\n\nconst PH_ITERATIONS = 100000;\nconst PH_KEY_LENGTH = 64;\nconst PH_DIGEST = 'sha512';\n\n/**\n * Hash password with PBKDF2 and random salt\n */\nasync function hashPassword(password: string): Promise<string> {\n const { pbkdf2, randomBytes: rb } = await import('node:crypto');\n const salt = rb(16).toString('hex');\n\n return new Promise((resolve, reject) => {\n pbkdf2(password, salt, PH_ITERATIONS, PH_KEY_LENGTH, PH_DIGEST, (err, derivedKey) => {\n if (err) reject(err);\n else resolve(`${salt}:${derivedKey.toString('hex')}`);\n });\n });\n}\n\n/**\n * Verify password against stored hash\n */\nasync function verifyPassword(password: string, storedHash: string): Promise<boolean> {\n const { pbkdf2, timingSafeEqual: tse } = await import('node:crypto');\n const [salt, hash] = storedHash.split(':');\n\n if (!(salt && hash)) {\n return false;\n }\n\n return new Promise((resolve, reject) => {\n pbkdf2(password, salt, PH_ITERATIONS, PH_KEY_LENGTH, PH_DIGEST, (err, derivedKey) => {\n if (err) reject(err);\n else {\n const derived = Buffer.from(derivedKey.toString('hex'), 'utf-8');\n const expected = Buffer.from(hash, 'utf-8');\n if (derived.length !== expected.length) {\n resolve(false);\n } else {\n resolve(tse(derived, expected));\n }\n }\n });\n });\n}\n\nexport const PasswordHasher = {\n hash: hashPassword,\n verify: verifyPassword,\n} as const;\n\n/**\n * Two-factor authentication\n */\n\n/**\n * Base32 encode\n */\nfunction base32Encode(buffer: Uint8Array): string {\n const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';\n let result = '';\n let bits = 0;\n let value = 0;\n\n for (const byte of buffer) {\n if (byte === undefined) continue;\n value = (value << 8) | byte;\n bits += 8;\n\n while (bits >= 5) {\n result += alphabet[(value >>> (bits - 5)) & 31];\n bits -= 5;\n }\n }\n\n if (bits > 0) {\n result += alphabet[(value << (5 - bits)) & 31];\n }\n\n return result;\n}\n\n/**\n * Base32 decode (RFC 4648) — converts base32 string back to raw bytes.\n * Required by RFC 6238: the HMAC key must be the decoded binary secret,\n * not the base32-encoded string.\n */\nfunction base32Decode(encoded: string): Uint8Array {\n const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';\n const stripped = encoded.replace(/=+$/, '').toUpperCase();\n const bytes: number[] = [];\n let bits = 0;\n let value = 0;\n\n for (const char of stripped) {\n const idx = alphabet.indexOf(char);\n if (idx === -1) continue;\n value = (value << 5) | idx;\n bits += 5;\n\n if (bits >= 8) {\n bytes.push((value >>> (bits - 8)) & 0xff);\n bits -= 8;\n }\n }\n\n return new Uint8Array(bytes);\n}\n\n/**\n * Encode a 64-bit counter as an 8-byte big-endian buffer (RFC 4226 §5.2).\n * Standard authenticator apps expect this encoding — NOT a decimal string.\n */\nfunction counterToBytes(counter: number): Buffer {\n const buf = Buffer.alloc(8);\n // Write as two 32-bit big-endian integers (JS numbers are safe up to 2^53)\n buf.writeUInt32BE(Math.floor(counter / 0x100000000), 0);\n buf.writeUInt32BE(counter >>> 0, 4);\n return buf;\n}\n\n/**\n * HMAC-SHA1 for TOTP (RFC 6238 §4).\n * Key: raw decoded bytes. Message: 8-byte big-endian counter.\n */\nfunction totpHmac(decodedKey: Uint8Array, counterBuf: Buffer): Uint8Array {\n const hmacDigest = createHmac('sha1', decodedKey).update(counterBuf).digest();\n return new Uint8Array(hmacDigest);\n}\n\n/**\n * Generate TOTP secret\n */\nfunction generateSecret(): string {\n const crypto = globalThis.crypto;\n if (!crypto) {\n throw new Error('Crypto API not available');\n }\n\n const buffer = new Uint8Array(20);\n crypto.getRandomValues(buffer);\n return base32Encode(buffer);\n}\n\n/**\n * Generate TOTP code (RFC 6238 compliant).\n * Secret is base32-encoded — decoded before HMAC.\n * Counter is encoded as 8-byte big-endian — matches all standard authenticator apps.\n */\nfunction generateCode(secret: string, timestamp?: number): string {\n const time = Math.floor((timestamp || Date.now()) / 30000);\n const decodedKey = base32Decode(secret);\n const counterBuf = counterToBytes(time);\n const hmacDigest = totpHmac(decodedKey, counterBuf);\n // biome-ignore lint/style/noNonNullAssertion: HMAC-SHA1 always produces 20 bytes; buffer indices are guaranteed valid\n const offset = hmacDigest[hmacDigest.length - 1]! & 0x0f;\n // biome-ignore lint/style/noNonNullAssertion: HMAC-SHA1 always produces 20 bytes; buffer indices are guaranteed valid\n const b0 = hmacDigest[offset]! & 0x7f;\n // biome-ignore lint/style/noNonNullAssertion: HMAC-SHA1 always produces 20 bytes; buffer indices are guaranteed valid\n const b1 = hmacDigest[offset + 1]! & 0xff;\n // biome-ignore lint/style/noNonNullAssertion: HMAC-SHA1 always produces 20 bytes; buffer indices are guaranteed valid\n const b2 = hmacDigest[offset + 2]! & 0xff;\n // biome-ignore lint/style/noNonNullAssertion: HMAC-SHA1 always produces 20 bytes; buffer indices are guaranteed valid\n const b3 = hmacDigest[offset + 3]! & 0xff;\n const code = ((b0 << 24) | (b1 << 16) | (b2 << 8) | b3) % 1000000;\n\n return code.toString().padStart(6, '0');\n}\n\n/**\n * Verify TOTP code\n */\nfunction verifyCode(secret: string, code: string, window: number = 1): boolean {\n const timestamp = Date.now();\n\n // Check current and adjacent time windows\n for (let i = -window; i <= window; i++) {\n const testTime = timestamp + i * 30000;\n const testCode = generateCode(secret, testTime);\n\n if (\n testCode.length === code.length &&\n timingSafeEqual(Buffer.from(testCode), Buffer.from(code))\n ) {\n return true;\n }\n }\n\n return false;\n}\n\nexport const TwoFactorAuth = {\n generateSecret,\n generateCode,\n verifyCode,\n} as const;\n","/**\n * Authorization System\n *\n * Role-Based Access Control (RBAC) and Attribute-Based Access Control (ABAC)\n */\n\nexport interface Permission {\n resource: string;\n action: string;\n conditions?: Record<string, unknown>;\n}\n\nexport interface Role {\n id: string;\n name: string;\n description?: string;\n permissions: Permission[];\n inherits?: string[];\n}\n\nexport interface Policy {\n id: string;\n name: string;\n effect: 'allow' | 'deny';\n resources: string[];\n actions: string[];\n conditions?: PolicyCondition[];\n priority?: number;\n}\n\nexport interface PolicyCondition {\n field: string;\n operator: 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'contains';\n value: unknown;\n}\n\nexport interface AuthorizationContext {\n user: {\n id: string;\n roles: string[];\n attributes?: Record<string, unknown>;\n };\n resource?: {\n type: string;\n id?: string;\n owner?: string;\n attributes?: Record<string, unknown>;\n };\n environment?: {\n time?: Date;\n ip?: string;\n userAgent?: string;\n };\n}\n\n/**\n * Authorization system\n */\nexport class AuthorizationSystem {\n private roles: Map<string, Role> = new Map();\n private policies: Map<string, Policy> = new Map();\n\n /**\n * Register role\n */\n registerRole(role: Role): void {\n this.roles.set(role.id, role);\n }\n\n /**\n * Get role\n */\n getRole(roleId: string): Role | undefined {\n return this.roles.get(roleId);\n }\n\n /**\n * Register policy\n */\n registerPolicy(policy: Policy): void {\n this.policies.set(policy.id, policy);\n }\n\n /**\n * Check if user has permission (RBAC)\n */\n hasPermission(userRoles: string[], resource: string, action: string): boolean {\n // Get all permissions for user's roles\n const permissions = this.getUserPermissions(userRoles);\n\n // Check if any permission matches\n return permissions.some(\n (permission) =>\n this.matchesResource(permission.resource, resource) &&\n this.matchesAction(permission.action, action),\n );\n }\n\n /**\n * Check access with policies (ABAC)\n */\n checkAccess(\n context: AuthorizationContext,\n resource: string,\n action: string,\n ): { allowed: boolean; reason?: string } {\n // Check RBAC first\n if (this.hasPermission(context.user.roles, resource, action)) {\n return { allowed: true };\n }\n\n // Check policies\n const applicablePolicies = this.getApplicablePolicies(resource, action, context);\n\n // Sort by priority (higher priority first)\n applicablePolicies.sort((a, b) => (b.priority || 0) - (a.priority || 0));\n\n // Apply first matching policy\n for (const policy of applicablePolicies) {\n if (this.evaluateConditions(policy.conditions || [], context)) {\n return {\n allowed: policy.effect === 'allow',\n reason: policy.effect === 'deny' ? `Denied by policy: ${policy.name}` : undefined,\n };\n }\n }\n\n return { allowed: false, reason: 'No matching policy' };\n }\n\n /**\n * Get all permissions for roles\n */\n private getUserPermissions(roleIds: string[]): Permission[] {\n const permissions: Permission[] = [];\n const visited = new Set<string>();\n\n const addRolePermissions = (roleId: string) => {\n if (visited.has(roleId)) return;\n\n visited.add(roleId);\n\n const role = this.roles.get(roleId);\n if (!role) return;\n\n // Add role permissions\n permissions.push(...role.permissions);\n\n // Add inherited permissions\n if (role.inherits) {\n role.inherits.forEach((inheritedRoleId) => {\n addRolePermissions(inheritedRoleId);\n });\n }\n };\n\n roleIds.forEach(addRolePermissions);\n\n return permissions;\n }\n\n /**\n * Get applicable policies\n */\n private getApplicablePolicies(\n resource: string,\n action: string,\n _context: AuthorizationContext,\n ): Policy[] {\n return Array.from(this.policies.values()).filter((policy) => {\n // Check if resource matches\n const resourceMatches = policy.resources.some((r) => this.matchesResource(r, resource));\n\n // Check if action matches\n const actionMatches = policy.actions.some((a) => this.matchesAction(a, action));\n\n return resourceMatches && actionMatches;\n });\n }\n\n /**\n * Match resource pattern\n */\n private matchesResource(pattern: string, resource: string): boolean {\n if (pattern === '*') return true;\n if (pattern === resource) return true;\n\n // Convert glob pattern to regex\n const regex = new RegExp(\n `^${pattern.replace(/\\./g, '\\\\.').replace(/\\*/g, '.*').replace(/\\?/g, '.')}$`,\n );\n\n return regex.test(resource);\n }\n\n /**\n * Match action pattern\n */\n private matchesAction(pattern: string, action: string): boolean {\n if (pattern === '*') return true;\n if (pattern === action) return true;\n\n // Support wildcards like \"read:*\"\n const regex = new RegExp(\n `^${pattern.replace(/\\./g, '\\\\.').replace(/\\*/g, '.*').replace(/\\?/g, '.')}$`,\n );\n\n return regex.test(action);\n }\n\n /**\n * Evaluate policy conditions\n */\n private evaluateConditions(\n conditions: PolicyCondition[],\n context: AuthorizationContext,\n ): boolean {\n return conditions.every((condition) => {\n const value = this.getContextValue(condition.field, context);\n return this.evaluateCondition(condition, value);\n });\n }\n\n /**\n * Get value from context\n */\n private getContextValue(field: string, context: AuthorizationContext): unknown {\n const parts = field.split('.');\n\n let value: unknown = context;\n\n for (const part of parts) {\n if (value && typeof value === 'object' && part in value) {\n value = (value as Record<string, unknown>)[part];\n } else {\n return undefined;\n }\n }\n\n return value;\n }\n\n /**\n * Evaluate single condition\n */\n private evaluateCondition(condition: PolicyCondition, value: unknown): boolean {\n switch (condition.operator) {\n case 'eq':\n return value === condition.value;\n\n case 'ne':\n return value !== condition.value;\n\n case 'gt':\n return typeof value === 'number' && value > (condition.value as number);\n\n case 'gte':\n return typeof value === 'number' && value >= (condition.value as number);\n\n case 'lt':\n return typeof value === 'number' && value < (condition.value as number);\n\n case 'lte':\n return typeof value === 'number' && value <= (condition.value as number);\n\n case 'in':\n return Array.isArray(condition.value) && condition.value.includes(value);\n\n case 'contains':\n return (\n typeof value === 'string' &&\n typeof condition.value === 'string' &&\n value.includes(condition.value)\n );\n\n default:\n return false;\n }\n }\n\n /**\n * Check if user owns resource\n */\n ownsResource(userId: string, resource: { owner?: string }): boolean {\n return resource.owner === userId;\n }\n\n /**\n * Clear all roles and policies\n */\n clear(): void {\n this.roles.clear();\n this.policies.clear();\n }\n}\n\n/**\n * Global authorization instance\n */\nexport const authorization = new AuthorizationSystem();\n\n/**\n * Common roles — aligned with DB schema (`users.role` column)\n * and `UserRoleSchema` in @revealui/contracts.\n *\n * Values: owner | admin | editor | viewer | agent | contributor\n */\nexport const CommonRoles: Record<string, Role> = {\n owner: {\n id: 'owner',\n name: 'Owner',\n description: 'Full control — inherits admin',\n permissions: [{ resource: '*', action: '*' }],\n inherits: ['admin'],\n },\n admin: {\n id: 'admin',\n name: 'Administrator',\n description: 'Full system access',\n permissions: [{ resource: '*', action: '*' }],\n },\n editor: {\n id: 'editor',\n name: 'Editor',\n description: 'Can read and modify content',\n permissions: [\n { resource: 'content', action: 'read' },\n { resource: 'content', action: 'create' },\n { resource: 'content', action: 'update' },\n { resource: 'profile', action: 'read' },\n { resource: 'profile', action: 'update' },\n { resource: 'sites', action: 'read' },\n { resource: 'marketplace', action: 'read' },\n ],\n },\n viewer: {\n id: 'viewer',\n name: 'Viewer',\n description: 'Read-only access',\n permissions: [\n { resource: 'content', action: 'read' },\n { resource: 'profile', action: 'read' },\n { resource: 'sites', action: 'read' },\n { resource: 'public', action: 'read' },\n ],\n },\n agent: {\n id: 'agent',\n name: 'AI Agent',\n description: 'Can execute tasks and read content',\n permissions: [\n { resource: 'tasks', action: 'create' },\n { resource: 'tasks', action: 'read' },\n { resource: 'content', action: 'read' },\n { resource: 'rag', action: 'read' },\n { resource: 'rag', action: 'create' },\n ],\n },\n contributor: {\n id: 'contributor',\n name: 'Contributor',\n description: 'Can suggest changes — create drafts but not publish or delete',\n permissions: [\n { resource: 'content', action: 'read' },\n { resource: 'content', action: 'create' },\n { resource: 'profile', action: 'read' },\n { resource: 'profile', action: 'update' },\n ],\n },\n} satisfies Record<string, Role>;\n\n/**\n * Permission builder\n */\nexport class PermissionBuilder {\n private permission: Partial<Permission> = {};\n\n resource(resource: string): this {\n this.permission.resource = resource;\n return this;\n }\n\n action(action: string): this {\n this.permission.action = action;\n return this;\n }\n\n conditions(conditions: Record<string, unknown>): this {\n this.permission.conditions = conditions;\n return this;\n }\n\n build(): Permission {\n if (!(this.permission.resource && this.permission.action)) {\n throw new Error('Resource and action are required');\n }\n\n return this.permission as Permission;\n }\n}\n\n/**\n * Policy builder\n */\nexport class PolicyBuilder {\n private policy: Partial<Policy> = {\n effect: 'allow',\n resources: [],\n actions: [],\n conditions: [],\n };\n\n id(id: string): this {\n this.policy.id = id;\n return this;\n }\n\n name(name: string): this {\n this.policy.name = name;\n return this;\n }\n\n allow(): this {\n this.policy.effect = 'allow';\n return this;\n }\n\n deny(): this {\n this.policy.effect = 'deny';\n return this;\n }\n\n resources(...resources: string[]): this {\n this.policy.resources = resources;\n return this;\n }\n\n actions(...actions: string[]): this {\n this.policy.actions = actions;\n return this;\n }\n\n condition(field: string, operator: PolicyCondition['operator'], value: unknown): this {\n if (!this.policy.conditions) {\n this.policy.conditions = [];\n }\n\n this.policy.conditions.push({ field, operator, value });\n return this;\n }\n\n priority(priority: number): this {\n this.policy.priority = priority;\n return this;\n }\n\n build(): Policy {\n if (!(this.policy.id && this.policy.name)) {\n throw new Error('ID and name are required');\n }\n\n return this.policy as Policy;\n }\n}\n\n/**\n * Authorization decorators\n */\nexport function RequirePermission(resource: string, action: string) {\n return (_target: object, _propertyKey: string, descriptor: PropertyDescriptor) => {\n const originalMethod = descriptor.value;\n\n descriptor.value = function (this: { user?: { roles?: string[] } }, ...args: unknown[]) {\n const userRoles = this.user?.roles || [];\n\n if (!authorization.hasPermission(userRoles, resource, action)) {\n throw new Error(`Permission denied: ${resource}:${action}`);\n }\n\n return originalMethod.apply(this, args);\n };\n\n return descriptor;\n };\n}\n\nexport function RequireRole(requiredRole: string) {\n return (_target: object, _propertyKey: string, descriptor: PropertyDescriptor) => {\n const originalMethod = descriptor.value;\n\n descriptor.value = function (this: { user?: { roles?: string[] } }, ...args: unknown[]) {\n const userRoles = this.user?.roles || [];\n\n if (!userRoles.includes(requiredRole)) {\n throw new Error(`Role required: ${requiredRole}`);\n }\n\n return originalMethod.apply(this, args);\n };\n\n return descriptor;\n };\n}\n\n/**\n * Authorization middleware\n */\nexport function createAuthorizationMiddleware<TRequest = unknown>(\n getUser: (request: TRequest) => { id: string; roles: string[] },\n resource: string,\n action: string,\n) {\n return (request: TRequest, next: () => Promise<unknown>) => {\n const user = getUser(request);\n\n if (!authorization.hasPermission(user.roles, resource, action)) {\n throw new Error(`Permission denied: ${resource}:${action}`);\n }\n\n return next();\n };\n}\n\n/**\n * Resource ownership check\n */\nexport function canAccessResource(\n userId: string,\n userRoles: string[],\n resource: {\n type: string;\n id?: string;\n owner?: string;\n },\n action: string,\n): boolean {\n // Check if user has permission\n if (authorization.hasPermission(userRoles, resource.type, action)) {\n return true;\n }\n\n // Check if user owns the resource\n if (authorization.ownsResource(userId, resource)) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Attribute-based access control helper\n */\nexport function checkAttributeAccess(\n context: AuthorizationContext,\n resource: string,\n action: string,\n requiredAttributes?: Record<string, unknown>,\n): boolean {\n // Check basic permission\n const { allowed } = authorization.checkAccess(context, resource, action);\n\n if (!allowed) {\n return false;\n }\n\n // Check required attributes\n if (requiredAttributes) {\n const userAttributes = context.user.attributes || {};\n\n return Object.entries(requiredAttributes).every(\n ([key, value]) => userAttributes[key] === value,\n );\n }\n\n return true;\n}\n\n/**\n * Permission cache for performance\n */\nexport class PermissionCache {\n private cache: Map<string, { allowed: boolean; expiresAt: number }> = new Map();\n private ttl: number;\n private maxEntries: number;\n\n constructor(ttl: number = 300000, maxEntries: number = 10_000) {\n // 5 minutes default, 10k max entries\n this.ttl = ttl;\n this.maxEntries = maxEntries;\n }\n\n /**\n * Get cached permission\n */\n get(userId: string, resource: string, action: string): boolean | undefined {\n const key = this.getCacheKey(userId, resource, action);\n const cached = this.cache.get(key);\n\n if (!cached) {\n return undefined;\n }\n\n // Check expiration\n if (Date.now() > cached.expiresAt) {\n this.cache.delete(key);\n return undefined;\n }\n\n return cached.allowed;\n }\n\n /**\n * Set cached permission\n */\n set(userId: string, resource: string, action: string, allowed: boolean): void {\n const key = this.getCacheKey(userId, resource, action);\n\n // Evict expired entries when approaching max size\n if (this.cache.size >= this.maxEntries) {\n const now = Date.now();\n for (const [k, v] of this.cache) {\n if (now > v.expiresAt) this.cache.delete(k);\n }\n // If still over limit after purge, drop oldest entries (FIFO via Map insertion order)\n if (this.cache.size >= this.maxEntries) {\n const excess = this.cache.size - this.maxEntries + 1;\n const keys = this.cache.keys();\n for (let i = 0; i < excess; i++) {\n const next = keys.next();\n if (!next.done) this.cache.delete(next.value);\n }\n }\n }\n\n this.cache.set(key, {\n allowed,\n expiresAt: Date.now() + this.ttl,\n });\n }\n\n /**\n * Clear cache for user\n */\n clearUser(userId: string): void {\n for (const key of this.cache.keys()) {\n if (key.startsWith(`${userId}:`)) {\n this.cache.delete(key);\n }\n }\n }\n\n /**\n * Clear all cache\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get cache key\n */\n private getCacheKey(userId: string, resource: string, action: string): string {\n return `${userId}:${resource}:${action}`;\n }\n}\n\n/**\n * Global permission cache\n */\nexport const permissionCache = new PermissionCache();\n","/**\n * Encryption Utilities\n *\n * Data encryption for at-rest and in-transit protection\n */\n\nexport interface EncryptionConfig {\n algorithm: 'AES-GCM' | 'AES-CTR';\n keySize: 128 | 192 | 256;\n ivSize?: number;\n /** Allow key export via exportKey(). Default: false (keys are non-extractable). */\n extractable?: boolean;\n}\n\nexport interface EncryptedData {\n data: string;\n iv: string;\n tag?: string;\n algorithm: string;\n}\n\nconst DEFAULT_CONFIG: EncryptionConfig = {\n algorithm: 'AES-GCM',\n keySize: 256,\n ivSize: 12,\n};\n\n/**\n * Encryption system\n */\nexport class EncryptionSystem {\n private config: EncryptionConfig;\n private keys: Map<string, CryptoKey> = new Map();\n\n constructor(config: Partial<EncryptionConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Generate encryption key\n */\n async generateKey(keyId?: string): Promise<CryptoKey> {\n const crypto = globalThis.crypto;\n if (!crypto) {\n throw new Error('Crypto API not available');\n }\n\n const key = await crypto.subtle.generateKey(\n {\n name: this.config.algorithm,\n length: this.config.keySize,\n },\n this.config.extractable ?? false, // non-extractable by default — prevents key exfiltration\n ['encrypt', 'decrypt'],\n );\n\n if (keyId) {\n this.keys.set(keyId, key);\n }\n\n return key;\n }\n\n /**\n * Import key from raw data\n */\n async importKey(keyData: ArrayBuffer, keyId?: string): Promise<CryptoKey> {\n const crypto = globalThis.crypto;\n if (!crypto) {\n throw new Error('Crypto API not available');\n }\n\n const key = await crypto.subtle.importKey(\n 'raw',\n keyData,\n {\n name: this.config.algorithm,\n length: this.config.keySize,\n },\n this.config.extractable ?? false, // non-extractable by default — prevents key exfiltration\n ['encrypt', 'decrypt'],\n );\n\n if (keyId) {\n this.keys.set(keyId, key);\n }\n\n return key;\n }\n\n /**\n * Export key to raw data\n */\n async exportKey(key: CryptoKey): Promise<ArrayBuffer> {\n const crypto = globalThis.crypto;\n if (!crypto) {\n throw new Error('Crypto API not available');\n }\n\n return crypto.subtle.exportKey('raw', key);\n }\n\n /**\n * Encrypt data\n */\n async encrypt(data: string, keyOrId: CryptoKey | string): Promise<EncryptedData> {\n const crypto = globalThis.crypto;\n if (!crypto) {\n throw new Error('Crypto API not available');\n }\n\n // Get key\n const key = typeof keyOrId === 'string' ? this.keys.get(keyOrId) : keyOrId;\n\n if (!key) {\n throw new Error('Key not found');\n }\n\n // Generate IV\n const iv = crypto.getRandomValues(new Uint8Array(this.config.ivSize || 12));\n\n // Encode data\n const encoder = new TextEncoder();\n const encodedData = encoder.encode(data);\n\n // Encrypt\n const encrypted = await crypto.subtle.encrypt(\n {\n name: this.config.algorithm,\n iv,\n },\n key,\n encodedData,\n );\n\n // Convert to base64\n const encryptedArray = new Uint8Array(encrypted);\n const ivArray = new Uint8Array(iv);\n\n return {\n data: this.arrayBufferToBase64(encryptedArray),\n iv: this.arrayBufferToBase64(ivArray),\n algorithm: this.config.algorithm,\n };\n }\n\n /**\n * Decrypt data\n */\n async decrypt(encryptedData: EncryptedData, keyOrId: CryptoKey | string): Promise<string> {\n const crypto = globalThis.crypto;\n if (!crypto) {\n throw new Error('Crypto API not available');\n }\n\n // Get key\n const key = typeof keyOrId === 'string' ? this.keys.get(keyOrId) : keyOrId;\n\n if (!key) {\n throw new Error('Key not found');\n }\n\n // Decode data\n const data = this.base64ToArrayBuffer(encryptedData.data);\n const iv = this.base64ToArrayBuffer(encryptedData.iv);\n\n // Decrypt\n const decrypted = await crypto.subtle.decrypt(\n {\n name: encryptedData.algorithm,\n iv: iv as BufferSource,\n },\n key,\n data as BufferSource,\n );\n\n // Decode text\n const decoder = new TextDecoder();\n return decoder.decode(decrypted);\n }\n\n /**\n * Encrypt object\n */\n async encryptObject<T extends Record<string, unknown>>(\n obj: T,\n keyOrId: CryptoKey | string,\n ): Promise<EncryptedData> {\n const json = JSON.stringify(obj);\n return this.encrypt(json, keyOrId);\n }\n\n /**\n * Decrypt object\n */\n async decryptObject<T extends Record<string, unknown>>(\n encryptedData: EncryptedData,\n keyOrId: CryptoKey | string,\n ): Promise<T> {\n const json = await this.decrypt(encryptedData, keyOrId);\n return JSON.parse(json);\n }\n\n /**\n * Hash data\n */\n async hash(\n data: string,\n algorithm: 'SHA-256' | 'SHA-384' | 'SHA-512' = 'SHA-256',\n ): Promise<string> {\n const crypto = globalThis.crypto;\n if (!crypto) {\n throw new Error('Crypto API not available');\n }\n\n const encoder = new TextEncoder();\n const encodedData = encoder.encode(data);\n const hashBuffer = await crypto.subtle.digest(algorithm, encodedData);\n\n return this.arrayBufferToBase64(new Uint8Array(hashBuffer));\n }\n\n /**\n * Generate random bytes\n */\n randomBytes(length: number): Uint8Array {\n const crypto = globalThis.crypto;\n if (!crypto) {\n throw new Error('Crypto API not available');\n }\n\n return crypto.getRandomValues(new Uint8Array(length));\n }\n\n /**\n * Generate random string\n */\n randomString(\n length: number,\n charset: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',\n ): string {\n // Rejection sampling to avoid modulo bias:\n // Only accept bytes below the largest multiple of charset.length that fits in a byte.\n const maxValid = 256 - (256 % charset.length);\n const result: string[] = [];\n while (result.length < length) {\n const bytes = this.randomBytes(length - result.length + 16);\n for (const byte of bytes) {\n if (byte < maxValid) {\n result.push(charset[byte % charset.length] as string);\n if (result.length === length) break;\n }\n }\n }\n return result.join('');\n }\n\n /**\n * Convert ArrayBuffer to base64\n */\n private arrayBufferToBase64(buffer: Uint8Array): string {\n const bytes = Array.from(buffer);\n const binary = bytes.map((byte) => String.fromCharCode(byte)).join('');\n\n if (typeof btoa !== 'undefined') {\n return btoa(binary);\n }\n\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(binary, 'binary').toString('base64');\n }\n\n throw new Error('No base64 encoding available');\n }\n\n /**\n * Convert base64 to ArrayBuffer\n */\n private base64ToArrayBuffer(base64: string): Uint8Array {\n let binary: string;\n\n if (typeof atob !== 'undefined') {\n binary = atob(base64);\n } else if (typeof Buffer !== 'undefined') {\n binary = Buffer.from(base64, 'base64').toString('binary');\n } else {\n throw new Error('No base64 decoding available');\n }\n\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n\n return bytes;\n }\n\n /**\n * Store key\n */\n storeKey(keyId: string, key: CryptoKey): void {\n this.keys.set(keyId, key);\n }\n\n /**\n * Get key\n */\n getKey(keyId: string): CryptoKey | undefined {\n return this.keys.get(keyId);\n }\n\n /**\n * Remove key\n */\n removeKey(keyId: string): void {\n this.keys.delete(keyId);\n }\n\n /**\n * Clear all keys\n */\n clearKeys(): void {\n this.keys.clear();\n }\n}\n\n/**\n * Global encryption instance\n */\nexport const encryption = new EncryptionSystem();\n\n/**\n * Field-level encryption\n */\nexport class FieldEncryption {\n private encryption: EncryptionSystem;\n private key: CryptoKey | null = null;\n\n constructor(encryption: EncryptionSystem) {\n this.encryption = encryption;\n }\n\n /**\n * Initialize with key\n */\n async initialize(key: CryptoKey): Promise<void> {\n this.key = key;\n }\n\n /**\n * Encrypt field\n */\n async encryptField(value: unknown): Promise<EncryptedData> {\n if (!this.key) {\n throw new Error('Encryption not initialized');\n }\n\n const stringValue = typeof value === 'string' ? value : JSON.stringify(value);\n return this.encryption.encrypt(stringValue, this.key);\n }\n\n /**\n * Decrypt field\n */\n async decryptField(encryptedData: EncryptedData): Promise<unknown> {\n if (!this.key) {\n throw new Error('Encryption not initialized');\n }\n\n const decrypted = await this.encryption.decrypt(encryptedData, this.key);\n\n // Try to parse as JSON\n try {\n return JSON.parse(decrypted);\n } catch {\n return decrypted;\n }\n }\n\n /**\n * Encrypt object fields\n */\n async encryptFields<T extends Record<string, unknown>>(obj: T, fields: (keyof T)[]): Promise<T> {\n const result = { ...obj };\n\n for (const field of fields) {\n if (field in result) {\n result[field] = (await this.encryptField(result[field])) as unknown as T[keyof T];\n }\n }\n\n return result;\n }\n\n /**\n * Decrypt object fields\n */\n async decryptFields<T extends Record<string, unknown>>(obj: T, fields: (keyof T)[]): Promise<T> {\n const result = { ...obj };\n\n for (const field of fields) {\n if (field in result && typeof result[field] === 'object' && result[field] !== null) {\n const encryptedData = result[field] as unknown as EncryptedData;\n if ('data' in encryptedData && 'iv' in encryptedData) {\n result[field] = (await this.decryptField(encryptedData)) as unknown as T[keyof T];\n }\n }\n }\n\n return result;\n }\n}\n\n/**\n * Key rotation\n */\nexport class KeyRotationManager {\n private encryption: EncryptionSystem;\n private currentKeyId: string;\n private oldKeys: Map<string, CryptoKey> = new Map();\n private keyCreationDates: Map<string, Date> = new Map();\n\n constructor(encryption: EncryptionSystem, initialKeyId: string) {\n this.encryption = encryption;\n this.currentKeyId = initialKeyId;\n this.keyCreationDates.set(initialKeyId, new Date());\n }\n\n /**\n * Rotate to new key\n */\n async rotate(newKeyId: string, newKey: CryptoKey): Promise<void> {\n // Store old key\n const oldKey = this.encryption.getKey(this.currentKeyId);\n if (oldKey) {\n this.oldKeys.set(this.currentKeyId, oldKey);\n }\n\n // Set new key\n this.encryption.storeKey(newKeyId, newKey);\n this.currentKeyId = newKeyId;\n this.keyCreationDates.set(newKeyId, new Date());\n }\n\n /**\n * Re-encrypt data with new key\n */\n async reencrypt(encryptedData: EncryptedData, oldKeyId: string): Promise<EncryptedData> {\n // Get keys\n const oldKey = this.oldKeys.get(oldKeyId) || this.encryption.getKey(oldKeyId);\n const newKey = this.encryption.getKey(this.currentKeyId);\n\n if (!(oldKey && newKey)) {\n throw new Error('Keys not found');\n }\n\n // Decrypt with old key\n const decrypted = await this.encryption.decrypt(encryptedData, oldKey);\n\n // Encrypt with new key\n return this.encryption.encrypt(decrypted, newKey);\n }\n\n /**\n * Get current key ID\n */\n getCurrentKeyId(): string {\n return this.currentKeyId;\n }\n\n /**\n * Clean up old keys created before the specified date.\n * Never removes the current active key.\n */\n cleanupOldKeys(olderThan: Date): void {\n for (const [keyId, createdAt] of this.keyCreationDates.entries()) {\n if (keyId !== this.currentKeyId && createdAt < olderThan) {\n this.oldKeys.delete(keyId);\n this.encryption.removeKey(keyId);\n this.keyCreationDates.delete(keyId);\n }\n }\n }\n}\n\n/**\n * Envelope encryption for large data\n */\nexport class EnvelopeEncryption {\n private encryption: EncryptionSystem;\n private masterKey: CryptoKey;\n\n constructor(encryption: EncryptionSystem, masterKey: CryptoKey) {\n this.encryption = encryption;\n this.masterKey = masterKey;\n }\n\n /**\n * Encrypt with envelope encryption\n */\n async encrypt(data: string): Promise<{\n encryptedData: EncryptedData;\n encryptedKey: EncryptedData;\n }> {\n // Generate data encryption key (DEK)\n const dek = await this.encryption.generateKey();\n\n // Encrypt data with DEK\n const encryptedData = await this.encryption.encrypt(data, dek);\n\n // Export DEK\n const dekRaw = await this.encryption.exportKey(dek);\n const dekBase64 = this.arrayBufferToBase64(new Uint8Array(dekRaw));\n\n // Encrypt DEK with master key\n const encryptedKey = await this.encryption.encrypt(dekBase64, this.masterKey);\n\n return { encryptedData, encryptedKey };\n }\n\n /**\n * Decrypt with envelope encryption\n */\n async decrypt(encryptedData: EncryptedData, encryptedKey: EncryptedData): Promise<string> {\n // Decrypt DEK with master key\n const dekBase64 = await this.encryption.decrypt(encryptedKey, this.masterKey);\n const dekRaw = this.base64ToArrayBuffer(dekBase64);\n\n // Import DEK\n const dek = await this.encryption.importKey(dekRaw.buffer as ArrayBuffer);\n\n // Decrypt data with DEK\n return this.encryption.decrypt(encryptedData, dek);\n }\n\n private arrayBufferToBase64(buffer: Uint8Array): string {\n const bytes = Array.from(buffer);\n const binary = bytes.map((byte) => String.fromCharCode(byte)).join('');\n return typeof btoa !== 'undefined'\n ? btoa(binary)\n : Buffer.from(binary, 'binary').toString('base64');\n }\n\n private base64ToArrayBuffer(base64: string): Uint8Array {\n const binary =\n typeof atob !== 'undefined' ? atob(base64) : Buffer.from(base64, 'base64').toString('binary');\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n }\n}\n\n/**\n * Data masking utilities\n */\n\n/**\n * Mask email\n */\nfunction maskEmail(email: string): string {\n const [local, domain] = email.split('@');\n if (!(local && domain)) return email;\n\n const maskedLocal =\n local.length > 2\n ? local[0] + '*'.repeat(local.length - 2) + local[local.length - 1]\n : `${local[0]}*`;\n\n return `${maskedLocal}@${domain}`;\n}\n\n/**\n * Mask phone number\n */\nfunction maskPhone(phone: string): string {\n const digits = phone.replace(/\\D/g, '');\n if (digits.length < 4) return phone;\n\n const lastFour = digits.slice(-4);\n const masked = '*'.repeat(digits.length - 4) + lastFour;\n\n return phone.replace(/\\d/g, (char, index) => {\n const digitIndex = phone.slice(0, index + 1).replace(/\\D/g, '').length - 1;\n return masked[digitIndex] || char;\n });\n}\n\n/**\n * Mask credit card\n */\nfunction maskCreditCard(card: string): string {\n const digits = card.replace(/\\D/g, '');\n if (digits.length < 4) return card;\n\n const lastFour = digits.slice(-4);\n return `****-****-****-${lastFour}`;\n}\n\n/**\n * Mask SSN\n */\nfunction maskSSN(ssn: string): string {\n const digits = ssn.replace(/\\D/g, '');\n if (digits.length !== 9) return ssn;\n\n return `***-**-${digits.slice(-4)}`;\n}\n\n/**\n * Mask string (keep first and last character)\n */\nfunction maskString(str: string, keepChars: number = 1): string {\n if (str.length <= keepChars * 2) {\n return '*'.repeat(str.length);\n }\n\n const prefix = str.slice(0, keepChars);\n const suffix = str.slice(-keepChars);\n const masked = '*'.repeat(str.length - keepChars * 2);\n\n return `${prefix}${masked}${suffix}`;\n}\n\nexport const DataMasking = {\n maskEmail,\n maskPhone,\n maskCreditCard,\n maskSSN,\n maskString,\n} as const;\n\n/**\n * Secure random token generator\n */\n\n/**\n * Generate secure token. `length` is the number of random bytes;\n * the returned string is hex-encoded, so it will be `length * 2` characters.\n */\nfunction generateToken(length: number = 32): string {\n const bytes = encryption.randomBytes(length);\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Generate UUID v4\n */\nfunction generateUUID(): string {\n const crypto = globalThis.crypto;\n if (!crypto) {\n throw new Error('Crypto API not available');\n }\n\n return crypto.randomUUID();\n}\n\n/**\n * Generate API key\n */\nfunction generateAPIKey(prefix: string = 'sk'): string {\n const token = generateToken(32);\n return `${prefix}_${token}`;\n}\n\n/**\n * Generate session ID\n */\nfunction generateSessionID(): string {\n return generateToken(64);\n}\n\nexport const TokenGenerator = {\n generate: generateToken,\n generateUUID,\n generateAPIKey,\n generateSessionID,\n} as const;\n","/**\n * GDPR Compliance Utilities\n *\n * Data privacy, consent management, data export, and right to be forgotten\n */\n\nimport { createHash, createHmac } from 'node:crypto';\nimport type { BreachStorage, GDPRStorage } from './gdpr-storage.js';\nimport { getSecurityLogger } from './logger.js';\n\nexport type ConsentType =\n | 'necessary'\n | 'functional'\n | 'analytics'\n | 'marketing'\n | 'personalization';\n\nexport type DataCategory =\n | 'personal'\n | 'sensitive'\n | 'financial'\n | 'health'\n | 'behavioral'\n | 'location';\n\nexport interface ConsentRecord {\n id: string;\n userId: string;\n type: ConsentType;\n granted: boolean;\n timestamp: string;\n expiresAt?: string;\n source: 'explicit' | 'implicit' | 'legitimate_interest';\n version: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface DataProcessingPurpose {\n id: string;\n name: string;\n description: string;\n legalBasis:\n | 'consent'\n | 'contract'\n | 'legal_obligation'\n | 'vital_interest'\n | 'public_interest'\n | 'legitimate_interest';\n dataCategories: DataCategory[];\n retentionPeriod: number; // days\n consentRequired: boolean;\n}\n\nexport interface PersonalDataExport {\n userId: string;\n exportedAt: string;\n data: {\n profile: Record<string, unknown>;\n activities: Record<string, unknown>[];\n consents: ConsentRecord[];\n dataProcessing: DataProcessingPurpose[];\n };\n format: 'json' | 'csv' | 'pdf';\n}\n\nexport interface DataDeletionRequest {\n id: string;\n userId: string;\n requestedAt: string;\n processedAt?: string;\n status: 'pending' | 'processing' | 'completed' | 'failed';\n dataCategories: DataCategory[];\n reason?: string;\n retainedData?: string[];\n deletedData?: string[];\n}\n\n/**\n * Consent management system\n */\nexport class ConsentManager {\n private readonly storage: GDPRStorage;\n private consentVersion: string = '1.0.0';\n\n constructor(storage: GDPRStorage) {\n this.storage = storage;\n }\n\n /**\n * Grant consent\n */\n async grantConsent(\n userId: string,\n type: ConsentType,\n source: ConsentRecord['source'] = 'explicit',\n expiresIn?: number,\n ): Promise<ConsentRecord> {\n const consent: ConsentRecord = {\n id: crypto.randomUUID(),\n userId,\n type,\n granted: true,\n timestamp: new Date().toISOString(),\n expiresAt: expiresIn ? new Date(Date.now() + expiresIn).toISOString() : undefined,\n source,\n version: this.consentVersion,\n };\n\n await this.storage.setConsent(userId, type, consent);\n\n return consent;\n }\n\n /**\n * Revoke consent\n */\n async revokeConsent(userId: string, type: ConsentType): Promise<void> {\n const existing = await this.storage.getConsent(userId, type);\n\n if (existing) {\n existing.granted = false;\n existing.timestamp = new Date().toISOString();\n await this.storage.setConsent(userId, type, existing);\n }\n }\n\n /**\n * Check if consent is granted\n */\n async hasConsent(userId: string, type: ConsentType): Promise<boolean> {\n const consent = await this.storage.getConsent(userId, type);\n\n if (!consent?.granted) {\n return false;\n }\n\n // Check if expired\n if (consent.expiresAt && new Date(consent.expiresAt) < new Date()) {\n return false;\n }\n\n return true;\n }\n\n /**\n * Get all consents for user\n */\n async getUserConsents(userId: string): Promise<ConsentRecord[]> {\n return this.storage.getConsentsByUser(userId);\n }\n\n /**\n * Update consent version\n */\n setConsentVersion(version: string): void {\n this.consentVersion = version;\n }\n\n /**\n * Check if consent needs renewal\n */\n async needsRenewal(userId: string, type: ConsentType, maxAge: number): Promise<boolean> {\n const consent = await this.storage.getConsent(userId, type);\n\n if (!consent?.granted) {\n return true;\n }\n\n const age = Date.now() - new Date(consent.timestamp).getTime();\n return age >= maxAge;\n }\n\n /**\n * Get consent statistics\n */\n async getStatistics(): Promise<{\n total: number;\n granted: number;\n revoked: number;\n expired: number;\n byType: Record<ConsentType, number>;\n }> {\n const consents = await this.storage.getAllConsents();\n const now = new Date();\n\n const granted = consents.filter((c) => c.granted).length;\n const revoked = consents.filter((c) => !c.granted).length;\n const expired = consents.filter((c) => c.expiresAt && new Date(c.expiresAt) < now).length;\n\n const byType = consents.reduce(\n (acc, c) => {\n acc[c.type] = (acc[c.type] || 0) + 1;\n return acc;\n },\n {} as Record<ConsentType, number>,\n );\n\n return {\n total: consents.length,\n granted,\n revoked,\n expired,\n byType,\n };\n }\n}\n\n/**\n * Escape a value for safe CSV inclusion.\n * Prevents CSV injection by prefixing formula-triggering characters (=, +, -, @, \\t, \\r)\n * with a single quote, and escapes embedded quotes/commas per RFC 4180.\n */\nfunction escapeCsvField(value: string): string {\n // Prefix formula-triggering characters to prevent CSV injection in spreadsheet apps\n let safe = /^[=+\\-@\\t\\r]/.test(value) ? `'${value}` : value;\n // RFC 4180: escape double quotes by doubling them\n safe = safe.replace(/\"/g, '\"\"');\n // Always quote the field to handle commas, newlines, and quotes\n return `\"${safe}\"`;\n}\n\n/**\n * Data export system\n */\nexport class DataExportSystem {\n /**\n * Export user data\n */\n async exportUserData(\n userId: string,\n getUserData: (userId: string) => Promise<{\n profile: Record<string, unknown>;\n activities: Record<string, unknown>[];\n consents: ConsentRecord[];\n }>,\n format: PersonalDataExport['format'] = 'json',\n ): Promise<PersonalDataExport> {\n const data = await getUserData(userId);\n\n const exportData: PersonalDataExport = {\n userId,\n exportedAt: new Date().toISOString(),\n data: {\n profile: data.profile,\n activities: data.activities,\n consents: data.consents,\n dataProcessing: [],\n },\n format,\n };\n\n return exportData;\n }\n\n /**\n * Format export as JSON\n */\n formatAsJSON(exportData: PersonalDataExport): string {\n return JSON.stringify(exportData, null, 2);\n }\n\n /**\n * Format export as CSV\n */\n formatAsCSV(exportData: PersonalDataExport): string {\n const lines: string[] = [];\n\n // Profile data\n lines.push('Type,Key,Value');\n Object.entries(exportData.data.profile).forEach(([key, value]) => {\n lines.push(`Profile,${escapeCsvField(key)},${escapeCsvField(String(value))}`);\n });\n\n // Activities\n exportData.data.activities.forEach((activity, index) => {\n Object.entries(activity).forEach(([key, value]) => {\n lines.push(`Activity ${index + 1},${escapeCsvField(key)},${escapeCsvField(String(value))}`);\n });\n });\n\n return lines.join('\\n');\n }\n\n /**\n * Create download link\n */\n createDownloadLink(content: string, _filename: string, mimeType: string): string {\n const blob = new Blob([content], { type: mimeType });\n return URL.createObjectURL(blob);\n }\n}\n\n/**\n * Data deletion system (Right to be Forgotten)\n */\nexport class DataDeletionSystem {\n private readonly storage: GDPRStorage;\n\n constructor(storage: GDPRStorage) {\n this.storage = storage;\n }\n\n /**\n * Request data deletion\n */\n async requestDeletion(\n userId: string,\n dataCategories: DataCategory[],\n reason?: string,\n ): Promise<DataDeletionRequest> {\n const request: DataDeletionRequest = {\n id: crypto.randomUUID(),\n userId,\n requestedAt: new Date().toISOString(),\n status: 'pending',\n dataCategories,\n reason,\n };\n\n await this.storage.setDeletionRequest(request);\n\n return request;\n }\n\n /**\n * Process deletion request\n */\n async processDeletion(\n requestId: string,\n deleteData: (\n userId: string,\n categories: DataCategory[],\n ) => Promise<{\n deleted: string[];\n retained: string[];\n }>,\n ): Promise<void> {\n const request = await this.storage.getDeletionRequest(requestId);\n\n if (!request) {\n throw new Error('Deletion request not found');\n }\n\n request.status = 'processing';\n await this.storage.setDeletionRequest(request);\n\n try {\n const result = await deleteData(request.userId, request.dataCategories);\n\n request.status = 'completed';\n request.processedAt = new Date().toISOString();\n request.deletedData = result.deleted;\n request.retainedData = result.retained;\n await this.storage.setDeletionRequest(request);\n } catch (error) {\n request.status = 'failed';\n await this.storage.setDeletionRequest(request);\n throw error;\n }\n }\n\n /**\n * Get deletion request\n */\n async getRequest(requestId: string): Promise<DataDeletionRequest | undefined> {\n return this.storage.getDeletionRequest(requestId);\n }\n\n /**\n * Get user deletion requests\n */\n async getUserRequests(userId: string): Promise<DataDeletionRequest[]> {\n return this.storage.getDeletionRequestsByUser(userId);\n }\n\n /**\n * Check if data can be deleted\n */\n canDelete(_dataCategory: DataCategory, legalBasis: DataProcessingPurpose['legalBasis']): boolean {\n // Data with legal obligation or vital interest cannot be deleted\n if (legalBasis === 'legal_obligation' || legalBasis === 'vital_interest') {\n return false;\n }\n\n return true;\n }\n\n /**\n * Calculate retention period\n */\n calculateRetentionEnd(createdAt: Date, retentionPeriod: number): Date {\n return new Date(createdAt.getTime() + retentionPeriod * 24 * 60 * 60 * 1000);\n }\n\n /**\n * Check if data should be deleted (retention period expired)\n */\n shouldDelete(createdAt: Date, retentionPeriod: number): boolean {\n const retentionEnd = this.calculateRetentionEnd(createdAt, retentionPeriod);\n return new Date() > retentionEnd;\n }\n}\n\n/**\n * Data anonymization utilities\n */\n\n/**\n * Hash value (irreversible) using SHA-256\n */\nfunction hashValue(value: string): string {\n const digest = createHash('sha256').update(value).digest('hex');\n return `hash_${digest}`;\n}\n\n/**\n * Anonymize user data\n */\nfunction anonymizeUser(user: Record<string, unknown>): Record<string, unknown> {\n return {\n ...user,\n email: hashValue(user.email as string),\n name: 'Anonymous User',\n phone: undefined,\n address: undefined,\n ip: undefined,\n };\n}\n\n/**\n * Pseudonymize data (one-way, key-dependent)\n *\n * Uses HMAC-SHA256 — cryptographically bound to the key, resistant to\n * length-extension attacks and GPU brute-force (unlike plain SHA-256).\n */\nfunction pseudonymize(value: string, key: string): string {\n const hmac = createHmac('sha256', key).update(value).digest('hex');\n return `pseudo_${hmac.substring(0, 16)}`;\n}\n\n/**\n * Anonymize dataset\n */\nfunction anonymizeDataset<T extends Record<string, unknown>>(\n data: T[],\n sensitiveFields: (keyof T)[],\n): T[] {\n return data.map((item) => {\n const anonymized = { ...item };\n\n sensitiveFields.forEach((field) => {\n if (field in anonymized && typeof anonymized[field] === 'string') {\n anonymized[field] = hashValue(anonymized[field] as string) as T[keyof T];\n }\n });\n\n return anonymized;\n });\n}\n\n/**\n * K-anonymity check\n */\nfunction checkKAnonymity<T extends Record<string, unknown>>(\n data: T[],\n quasiIdentifiers: (keyof T)[],\n k: number,\n): boolean {\n // Group by quasi-identifiers\n const groups = new Map<string, number>();\n\n data.forEach((item) => {\n const key = quasiIdentifiers.map((field) => String(item[field])).join('|');\n\n groups.set(key, (groups.get(key) || 0) + 1);\n });\n\n // Check if all groups have at least k members\n return Array.from(groups.values()).every((count) => count >= k);\n}\n\nexport const DataAnonymization = {\n anonymizeUser,\n pseudonymize,\n hashValue,\n anonymizeDataset,\n checkKAnonymity,\n} as const;\n\n/**\n * Privacy policy manager\n */\nexport class PrivacyPolicyManager {\n private policies: Map<string, { version: string; content: string; effectiveDate: Date }> =\n new Map();\n private currentVersion: string = '1.0.0';\n\n /**\n * Add policy version\n */\n addPolicy(version: string, content: string, effectiveDate: Date): void {\n this.policies.set(version, { version, content, effectiveDate });\n this.currentVersion = version;\n }\n\n /**\n * Get current policy\n */\n getCurrentPolicy(): { version: string; content: string; effectiveDate: Date } | undefined {\n return this.policies.get(this.currentVersion);\n }\n\n /**\n * Get policy by version\n */\n getPolicy(\n version: string,\n ): { version: string; content: string; effectiveDate: Date } | undefined {\n return this.policies.get(version);\n }\n\n /**\n * Check if user accepted current policy\n */\n hasAcceptedCurrent(userAcceptedVersion: string): boolean {\n return userAcceptedVersion === this.currentVersion;\n }\n\n /**\n * Get all versions\n */\n getAllVersions(): string[] {\n return Array.from(this.policies.keys());\n }\n}\n\n/**\n * Cookie consent banner\n */\nexport interface CookieConsentConfig {\n necessary: boolean;\n functional: boolean;\n analytics: boolean;\n marketing: boolean;\n}\n\nexport class CookieConsentManager {\n private config: CookieConsentConfig = {\n necessary: true,\n functional: false,\n analytics: false,\n marketing: false,\n };\n\n /**\n * Set consent configuration\n */\n setConsent(config: Partial<CookieConsentConfig>): void {\n this.config = { ...this.config, ...config };\n this.saveToStorage();\n }\n\n /**\n * Get consent configuration\n */\n getConsent(): CookieConsentConfig {\n return { ...this.config };\n }\n\n /**\n * Check if specific consent is granted\n */\n hasConsent(type: keyof CookieConsentConfig): boolean {\n return this.config[type];\n }\n\n /**\n * Save to storage\n */\n private saveToStorage(): void {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem('cookie-consent', JSON.stringify(this.config));\n }\n }\n\n /**\n * Load from storage\n */\n loadFromStorage(): void {\n if (typeof localStorage !== 'undefined') {\n const stored = localStorage.getItem('cookie-consent');\n if (stored) {\n try {\n const parsed = JSON.parse(stored);\n // Validate shape before assigning — only accept known boolean fields\n // to prevent malicious scripts from injecting arbitrary config.\n if (typeof parsed === 'object' && parsed !== null) {\n this.config = {\n necessary: true, // always required\n analytics: typeof parsed.analytics === 'boolean' ? parsed.analytics : false,\n marketing: typeof parsed.marketing === 'boolean' ? parsed.marketing : false,\n functional: typeof parsed.functional === 'boolean' ? parsed.functional : true,\n };\n }\n } catch {\n // Ignore parse errors\n }\n }\n }\n }\n\n /**\n * Clear consent\n */\n clearConsent(): void {\n this.config = {\n necessary: true,\n functional: false,\n analytics: false,\n marketing: false,\n };\n\n if (typeof localStorage !== 'undefined') {\n localStorage.removeItem('cookie-consent');\n }\n }\n}\n\n/**\n * Data breach notification system\n */\nexport interface DataBreach {\n id: string;\n detectedAt: string;\n reportedAt?: string;\n type: 'unauthorized_access' | 'data_loss' | 'data_leak' | 'system_compromise';\n severity: 'low' | 'medium' | 'high' | 'critical';\n affectedUsers: string[];\n dataCategories: DataCategory[];\n description: string;\n mitigation?: string;\n status: 'detected' | 'investigating' | 'notified' | 'resolved';\n}\n\nexport class DataBreachManager {\n private readonly storage: BreachStorage;\n\n constructor(storage: BreachStorage) {\n this.storage = storage;\n }\n\n /**\n * Report data breach\n */\n async reportBreach(\n breach: Omit<DataBreach, 'id' | 'detectedAt' | 'status'>,\n ): Promise<DataBreach> {\n const fullBreach: DataBreach = {\n ...breach,\n id: crypto.randomUUID(),\n detectedAt: new Date().toISOString(),\n status: 'detected',\n };\n\n await this.storage.setBreach(fullBreach);\n\n // Auto-notify if critical\n if (fullBreach.severity === 'critical') {\n await this.notifyAuthorities(fullBreach);\n }\n\n return fullBreach;\n }\n\n /**\n * Notify authorities (required within 72 hours under GDPR)\n */\n async notifyAuthorities(breach: DataBreach): Promise<void> {\n await this.storage.updateBreach(breach.id, {\n reportedAt: new Date().toISOString(),\n status: 'notified',\n });\n\n // In production, integrate with data protection authority API\n getSecurityLogger().info('Breach reported to authorities', { breachId: breach.id });\n }\n\n /**\n * Notify affected users\n */\n async notifyAffectedUsers(\n breachId: string,\n notifyFn: (userId: string, breach: DataBreach) => Promise<void>,\n ): Promise<void> {\n const breach = await this.storage.getBreach(breachId);\n\n if (!breach) {\n throw new Error('Breach not found');\n }\n\n for (const userId of breach.affectedUsers) {\n await notifyFn(userId, breach);\n }\n }\n\n /**\n * Check if breach notification is required\n */\n requiresNotification(breach: DataBreach): boolean {\n // Notification required for high risk breaches\n return (\n breach.severity === 'high' ||\n breach.severity === 'critical' ||\n breach.dataCategories.includes('sensitive') ||\n breach.dataCategories.includes('financial')\n );\n }\n\n /**\n * Get breach\n */\n async getBreach(id: string): Promise<DataBreach | undefined> {\n return this.storage.getBreach(id);\n }\n\n /**\n * Get all breaches\n */\n async getAllBreaches(): Promise<DataBreach[]> {\n return this.storage.getAllBreaches();\n }\n}\n\n/**\n * Factory functions for GDPR subsystems.\n *\n * `ConsentManager` and `DataDeletionSystem` require a `GDPRStorage` implementation.\n * Use `InMemoryGDPRStorage` only in tests — production MUST use a database-backed store.\n *\n * `DataExportSystem`, `PrivacyPolicyManager`, `CookieConsentManager`, and\n * `DataBreachManager` are stateless or client-side only, so singletons are safe.\n */\nexport function createConsentManager(storage: GDPRStorage): ConsentManager {\n return new ConsentManager(storage);\n}\n\nexport function createDataDeletionSystem(storage: GDPRStorage): DataDeletionSystem {\n return new DataDeletionSystem(storage);\n}\n\nexport const dataExportSystem = new DataExportSystem();\nexport const privacyPolicyManager = new PrivacyPolicyManager();\nexport const cookieConsentManager = new CookieConsentManager();\nexport function createDataBreachManager(storage: BreachStorage): DataBreachManager {\n return new DataBreachManager(storage);\n}\n","/**\n * GDPR Storage Abstraction\n *\n * Record-oriented storage interface for GDPR compliance data.\n * Provides a clean seam for replacing the default in-memory implementation\n * with a database-backed store in production.\n */\n\nimport type { ConsentRecord, ConsentType, DataBreach, DataDeletionRequest } from './gdpr.js';\n\n/**\n * Storage interface for GDPR consent records and deletion requests.\n *\n * All methods are async to support database-backed implementations.\n * The default `InMemoryGDPRStorage` is suitable for testing and development\n * but must be replaced with a persistent store for production use.\n */\nexport interface GDPRStorage {\n // ── Consent Records ──────────────────────────────────────────────\n\n /**\n * Store or update a consent record, keyed by `userId:consentType`.\n */\n setConsent(userId: string, type: ConsentType, record: ConsentRecord): Promise<void>;\n\n /**\n * Retrieve a consent record by user and type. Returns `undefined` if not found.\n */\n getConsent(userId: string, type: ConsentType): Promise<ConsentRecord | undefined>;\n\n /**\n * Retrieve all consent records for a given user.\n */\n getConsentsByUser(userId: string): Promise<ConsentRecord[]>;\n\n /**\n * Retrieve every consent record in storage (used for aggregate statistics).\n */\n getAllConsents(): Promise<ConsentRecord[]>;\n\n // ── Deletion Requests ────────────────────────────────────────────\n\n /**\n * Store a deletion request, keyed by its `id`.\n */\n setDeletionRequest(request: DataDeletionRequest): Promise<void>;\n\n /**\n * Retrieve a deletion request by ID. Returns `undefined` if not found.\n */\n getDeletionRequest(requestId: string): Promise<DataDeletionRequest | undefined>;\n\n /**\n * Retrieve all deletion requests for a given user.\n */\n getDeletionRequestsByUser(userId: string): Promise<DataDeletionRequest[]>;\n}\n\n/**\n * Storage interface for data breach records.\n *\n * All methods are async to support database-backed implementations.\n * The default `InMemoryBreachStorage` is suitable for testing and development\n * but must be replaced with a persistent store for production GDPR compliance.\n */\nexport interface BreachStorage {\n /**\n * Store a data breach record.\n */\n setBreach(breach: DataBreach): Promise<void>;\n\n /**\n * Retrieve a breach by ID. Returns `undefined` if not found.\n */\n getBreach(id: string): Promise<DataBreach | undefined>;\n\n /**\n * Retrieve all breach records.\n */\n getAllBreaches(): Promise<DataBreach[]>;\n\n /**\n * Update an existing breach record (e.g., status change, add mitigation).\n */\n updateBreach(id: string, updates: Partial<DataBreach>): Promise<void>;\n}\n\n/**\n * In-memory implementation of `BreachStorage`.\n *\n * WARNING: All data is lost on process restart or serverless cold start.\n * GDPR requires breach records be retained — use database-backed storage in production.\n */\nexport class InMemoryBreachStorage implements BreachStorage {\n private breaches: Map<string, DataBreach> = new Map();\n\n async setBreach(breach: DataBreach): Promise<void> {\n this.breaches.set(breach.id, breach);\n }\n\n async getBreach(id: string): Promise<DataBreach | undefined> {\n return this.breaches.get(id);\n }\n\n async getAllBreaches(): Promise<DataBreach[]> {\n return Array.from(this.breaches.values());\n }\n\n async updateBreach(id: string, updates: Partial<DataBreach>): Promise<void> {\n const existing = this.breaches.get(id);\n if (existing) {\n this.breaches.set(id, { ...existing, ...updates });\n }\n }\n}\n\n/**\n * In-memory implementation of `GDPRStorage`.\n *\n * WARNING: All data is lost on process restart or serverless cold start.\n * Use this only for development, testing, or as a reference implementation.\n * Production deployments MUST supply a database-backed `GDPRStorage`.\n */\nexport class InMemoryGDPRStorage implements GDPRStorage {\n private consents: Map<string, ConsentRecord> = new Map();\n private deletionRequests: Map<string, DataDeletionRequest> = new Map();\n\n // ── Consent Records ──────────────────────────────────────────────\n\n async setConsent(userId: string, type: ConsentType, record: ConsentRecord): Promise<void> {\n this.consents.set(`${userId}:${type}`, record);\n }\n\n async getConsent(userId: string, type: ConsentType): Promise<ConsentRecord | undefined> {\n return this.consents.get(`${userId}:${type}`);\n }\n\n async getConsentsByUser(userId: string): Promise<ConsentRecord[]> {\n return Array.from(this.consents.values()).filter((c) => c.userId === userId);\n }\n\n async getAllConsents(): Promise<ConsentRecord[]> {\n return Array.from(this.consents.values());\n }\n\n // ── Deletion Requests ────────────────────────────────────────────\n\n async setDeletionRequest(request: DataDeletionRequest): Promise<void> {\n this.deletionRequests.set(request.id, request);\n }\n\n async getDeletionRequest(requestId: string): Promise<DataDeletionRequest | undefined> {\n return this.deletionRequests.get(requestId);\n }\n\n async getDeletionRequestsByUser(userId: string): Promise<DataDeletionRequest[]> {\n return Array.from(this.deletionRequests.values()).filter((r) => r.userId === userId);\n }\n}\n","/**\n * Security Headers and CORS Configuration\n *\n * HTTP security headers and CORS policy management\n */\n\nimport { getSecurityLogger } from './logger.js';\n\nexport interface SecurityHeadersConfig {\n contentSecurityPolicy?: string | ContentSecurityPolicyConfig;\n strictTransportSecurity?: boolean | HSTSConfig;\n xFrameOptions?: 'DENY' | 'SAMEORIGIN' | string;\n xContentTypeOptions?: boolean;\n referrerPolicy?: ReferrerPolicyValue;\n permissionsPolicy?: string | PermissionsPolicyConfig;\n crossOriginEmbedderPolicy?: 'require-corp' | 'credentialless';\n crossOriginOpenerPolicy?: 'same-origin' | 'same-origin-allow-popups' | 'unsafe-none';\n crossOriginResourcePolicy?: 'same-origin' | 'same-site' | 'cross-origin';\n}\n\nexport interface ContentSecurityPolicyConfig {\n defaultSrc?: string[];\n scriptSrc?: string[];\n styleSrc?: string[];\n imgSrc?: string[];\n fontSrc?: string[];\n connectSrc?: string[];\n frameSrc?: string[];\n objectSrc?: string[];\n mediaSrc?: string[];\n workerSrc?: string[];\n childSrc?: string[];\n formAction?: string[];\n frameAncestors?: string[];\n baseUri?: string[];\n manifestSrc?: string[];\n upgradeInsecureRequests?: boolean;\n blockAllMixedContent?: boolean;\n reportUri?: string;\n reportTo?: string;\n}\n\nexport interface HSTSConfig {\n maxAge: number;\n includeSubDomains?: boolean;\n preload?: boolean;\n}\n\nexport type ReferrerPolicyValue =\n | 'no-referrer'\n | 'no-referrer-when-downgrade'\n | 'origin'\n | 'origin-when-cross-origin'\n | 'same-origin'\n | 'strict-origin'\n | 'strict-origin-when-cross-origin'\n | 'unsafe-url';\n\nexport interface PermissionsPolicyConfig {\n accelerometer?: string[];\n ambientLightSensor?: string[];\n autoplay?: string[];\n battery?: string[];\n camera?: string[];\n displayCapture?: string[];\n documentDomain?: string[];\n encryptedMedia?: string[];\n fullscreen?: string[];\n geolocation?: string[];\n gyroscope?: string[];\n magnetometer?: string[];\n microphone?: string[];\n midi?: string[];\n payment?: string[];\n pictureInPicture?: string[];\n publicKeyCredentials?: string[];\n screenWakeLock?: string[];\n syncXhr?: string[];\n usb?: string[];\n webShare?: string[];\n xrSpatialTracking?: string[];\n}\n\nexport interface CORSConfig {\n origin?: string | string[] | ((origin: string) => boolean);\n methods?: string[];\n allowedHeaders?: string[];\n exposedHeaders?: string[];\n credentials?: boolean;\n maxAge?: number;\n preflightContinue?: boolean;\n optionsSuccessStatus?: number;\n}\n\n/**\n * Security headers manager\n */\nexport class SecurityHeaders {\n private config: SecurityHeadersConfig;\n\n constructor(config: SecurityHeadersConfig = {}) {\n this.config = config;\n }\n\n /**\n * Get all security headers\n */\n getHeaders(): Record<string, string> {\n const headers: Record<string, string> = {};\n\n // Content Security Policy\n if (this.config.contentSecurityPolicy) {\n headers['Content-Security-Policy'] = this.buildCSP(this.config.contentSecurityPolicy);\n }\n\n // Strict Transport Security\n if (this.config.strictTransportSecurity) {\n headers['Strict-Transport-Security'] = this.buildHSTS(this.config.strictTransportSecurity);\n }\n\n // X-Frame-Options\n if (this.config.xFrameOptions) {\n headers['X-Frame-Options'] = this.config.xFrameOptions;\n }\n\n // X-Content-Type-Options\n if (this.config.xContentTypeOptions !== false) {\n headers['X-Content-Type-Options'] = 'nosniff';\n }\n\n // Referrer-Policy\n if (this.config.referrerPolicy) {\n headers['Referrer-Policy'] = this.config.referrerPolicy;\n }\n\n // Permissions-Policy\n if (this.config.permissionsPolicy) {\n headers['Permissions-Policy'] = this.buildPermissionsPolicy(this.config.permissionsPolicy);\n }\n\n // Cross-Origin headers\n if (this.config.crossOriginEmbedderPolicy) {\n headers['Cross-Origin-Embedder-Policy'] = this.config.crossOriginEmbedderPolicy;\n }\n\n if (this.config.crossOriginOpenerPolicy) {\n headers['Cross-Origin-Opener-Policy'] = this.config.crossOriginOpenerPolicy;\n }\n\n if (this.config.crossOriginResourcePolicy) {\n headers['Cross-Origin-Resource-Policy'] = this.config.crossOriginResourcePolicy;\n }\n\n return headers;\n }\n\n /**\n * Build Content Security Policy header\n */\n private buildCSP(config: string | ContentSecurityPolicyConfig): string {\n if (typeof config === 'string') {\n return config;\n }\n\n const directives: string[] = [];\n\n const addDirective = (name: string, values?: string[]) => {\n if (values && values.length > 0) {\n directives.push(`${name} ${values.join(' ')}`);\n }\n };\n\n addDirective('default-src', config.defaultSrc);\n addDirective('script-src', config.scriptSrc);\n addDirective('style-src', config.styleSrc);\n addDirective('img-src', config.imgSrc);\n addDirective('font-src', config.fontSrc);\n addDirective('connect-src', config.connectSrc);\n addDirective('frame-src', config.frameSrc);\n addDirective('object-src', config.objectSrc);\n addDirective('media-src', config.mediaSrc);\n addDirective('worker-src', config.workerSrc);\n addDirective('child-src', config.childSrc);\n addDirective('form-action', config.formAction);\n addDirective('frame-ancestors', config.frameAncestors);\n addDirective('base-uri', config.baseUri);\n addDirective('manifest-src', config.manifestSrc);\n\n if (config.upgradeInsecureRequests) {\n directives.push('upgrade-insecure-requests');\n }\n\n if (config.blockAllMixedContent) {\n directives.push('block-all-mixed-content');\n }\n\n if (config.reportUri) {\n directives.push(`report-uri ${config.reportUri}`);\n }\n\n if (config.reportTo) {\n directives.push(`report-to ${config.reportTo}`);\n }\n\n return directives.join('; ');\n }\n\n /**\n * Build HSTS header\n */\n private buildHSTS(config: boolean | HSTSConfig): string {\n if (config === true) {\n return 'max-age=31536000; includeSubDomains';\n }\n\n if (config === false) {\n return '';\n }\n\n // config is now HSTSConfig\n const parts = [`max-age=${config.maxAge}`];\n\n if (config.includeSubDomains) {\n parts.push('includeSubDomains');\n }\n\n if (config.preload) {\n parts.push('preload');\n }\n\n return parts.join('; ');\n }\n\n /**\n * Build Permissions-Policy header\n */\n private buildPermissionsPolicy(config: string | PermissionsPolicyConfig): string {\n if (typeof config === 'string') {\n return config;\n }\n\n const policies: string[] = [];\n\n Object.entries(config).forEach(([feature, origins]) => {\n if (!origins || origins.length === 0) {\n policies.push(`${feature}=()`);\n } else if (origins.includes('*')) {\n policies.push(`${feature}=*`);\n } else {\n const originsList = origins.map((o: string) => `\"${o}\"`).join(' ');\n policies.push(`${feature}=(${originsList})`);\n }\n });\n\n return policies.join(', ');\n }\n\n /**\n * Apply headers to response\n */\n applyHeaders(response: Response): Response {\n const headers = this.getHeaders();\n\n Object.entries(headers).forEach(([name, value]) => {\n response.headers.set(name, value);\n });\n\n return response;\n }\n}\n\n/**\n * CORS manager\n */\nexport class CORSManager {\n private config: Required<CORSConfig>;\n\n constructor(config: CORSConfig = {}) {\n this.config = {\n origin: config.origin ?? [],\n methods: config.methods || ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],\n allowedHeaders: config.allowedHeaders || ['Content-Type', 'Authorization'],\n exposedHeaders: config.exposedHeaders || [],\n credentials: config.credentials ?? false,\n maxAge: config.maxAge || 86400,\n preflightContinue: config.preflightContinue ?? false,\n optionsSuccessStatus: config.optionsSuccessStatus || 204,\n };\n }\n\n /**\n * Check if origin is allowed\n */\n isOriginAllowed(origin: string): boolean {\n const { origin: allowedOrigin } = this.config;\n\n if (allowedOrigin === '*') {\n return true;\n }\n\n if (typeof allowedOrigin === 'function') {\n return allowedOrigin(origin);\n }\n\n if (typeof allowedOrigin === 'string') {\n return origin === allowedOrigin;\n }\n\n if (Array.isArray(allowedOrigin)) {\n return allowedOrigin.includes(origin);\n }\n\n return false;\n }\n\n /**\n * Get CORS headers\n */\n getCORSHeaders(origin: string): Record<string, string> {\n const headers: Record<string, string> = {};\n\n // Vary: Origin — always set when origin is not '*' so caches\n // don't serve a response allowed for origin A to origin B.\n if (this.config.origin !== '*') {\n headers.Vary = 'Origin';\n }\n\n // All Access-Control-Allow-* headers must only be sent for allowed origins.\n // Sending them for disallowed origins leaks CORS policy details.\n if (!this.isOriginAllowed(origin)) {\n return headers;\n }\n\n // Access-Control-Allow-Origin\n headers['Access-Control-Allow-Origin'] = this.config.origin === '*' ? '*' : origin;\n\n // Access-Control-Allow-Credentials — incompatible with origin: '*' per Fetch spec\n if (this.config.credentials && this.config.origin !== '*') {\n headers['Access-Control-Allow-Credentials'] = 'true';\n }\n\n // Access-Control-Expose-Headers\n if (this.config.exposedHeaders.length > 0) {\n headers['Access-Control-Expose-Headers'] = this.config.exposedHeaders.join(', ');\n }\n\n return headers;\n }\n\n /**\n * Get preflight headers\n */\n getPreflightHeaders(origin: string): Record<string, string> {\n const headers = this.getCORSHeaders(origin);\n\n // Only include preflight-specific headers when the origin is allowed.\n // getCORSHeaders already returns early (with only Vary) for disallowed origins,\n // so we guard here as well to avoid leaking allowed methods/headers.\n if (!this.isOriginAllowed(origin)) {\n return headers;\n }\n\n // Access-Control-Allow-Methods\n headers['Access-Control-Allow-Methods'] = this.config.methods.join(', ');\n\n // Access-Control-Allow-Headers\n headers['Access-Control-Allow-Headers'] = this.config.allowedHeaders.join(', ');\n\n // Access-Control-Max-Age\n headers['Access-Control-Max-Age'] = this.config.maxAge.toString();\n\n return headers;\n }\n\n /**\n * Handle CORS request\n */\n handleRequest(request: Request): Response | null {\n const origin = request.headers.get('Origin');\n\n if (!origin) {\n return null;\n }\n\n // Handle preflight\n if (request.method === 'OPTIONS') {\n return this.handlePreflight(request, origin);\n }\n\n return null;\n }\n\n /**\n * Handle preflight request\n */\n handlePreflight(_request: Request, origin: string): Response {\n if (!this.isOriginAllowed(origin)) {\n return new Response(null, { status: 403 });\n }\n\n const headers = this.getPreflightHeaders(origin);\n\n return new Response(null, {\n status: this.config.optionsSuccessStatus,\n headers,\n });\n }\n\n /**\n * Apply CORS headers to response\n */\n applyHeaders(response: Response, origin: string): Response {\n if (!this.isOriginAllowed(origin)) {\n return response;\n }\n\n const headers = this.getCORSHeaders(origin);\n\n Object.entries(headers).forEach(([name, value]) => {\n response.headers.set(name, value);\n });\n\n return response;\n }\n}\n\n/**\n * Common security header presets\n */\nexport const SecurityPresets = {\n /**\n * Strict security (recommended for production)\n */\n strict: (): SecurityHeadersConfig => ({\n contentSecurityPolicy: {\n defaultSrc: [\"'self'\"],\n scriptSrc: [\"'self'\"],\n styleSrc: [\"'self'\", \"'unsafe-inline'\"],\n imgSrc: [\"'self'\", 'data:', 'https:'],\n fontSrc: [\"'self'\", 'data:'],\n connectSrc: [\"'self'\"],\n frameSrc: [\"'none'\"],\n objectSrc: [\"'none'\"],\n baseUri: [\"'self'\"],\n formAction: [\"'self'\"],\n frameAncestors: [\"'none'\"],\n upgradeInsecureRequests: true,\n },\n strictTransportSecurity: {\n maxAge: 31536000,\n includeSubDomains: true,\n preload: true,\n },\n xFrameOptions: 'DENY',\n xContentTypeOptions: true,\n referrerPolicy: 'strict-origin-when-cross-origin',\n crossOriginEmbedderPolicy: 'require-corp',\n crossOriginOpenerPolicy: 'same-origin',\n crossOriginResourcePolicy: 'same-origin',\n }),\n\n /**\n * Moderate security (balanced)\n */\n moderate: (): SecurityHeadersConfig => ({\n contentSecurityPolicy: {\n defaultSrc: [\"'self'\"],\n scriptSrc: [\"'self'\", \"'unsafe-inline'\"],\n styleSrc: [\"'self'\", \"'unsafe-inline'\"],\n imgSrc: [\"'self'\", 'data:', 'https:'],\n fontSrc: [\"'self'\", 'data:', 'https:'],\n connectSrc: [\"'self'\", 'https:'],\n frameAncestors: [\"'self'\"],\n },\n strictTransportSecurity: {\n maxAge: 31536000,\n includeSubDomains: true,\n },\n xFrameOptions: 'SAMEORIGIN',\n xContentTypeOptions: true,\n referrerPolicy: 'origin-when-cross-origin',\n }),\n\n /**\n * Development (permissive)\n */\n development: (): SecurityHeadersConfig => ({\n xContentTypeOptions: true,\n referrerPolicy: 'no-referrer-when-downgrade',\n }),\n};\n\n/**\n * Common CORS presets\n */\nexport const CORSPresets = {\n /**\n * Strict CORS (same origin only)\n */\n strict: (): CORSConfig => ({\n origin: [],\n methods: ['GET', 'POST', 'PUT', 'DELETE'],\n allowedHeaders: ['Content-Type', 'Authorization'],\n credentials: true,\n maxAge: 86400,\n }),\n\n /**\n * Moderate CORS (specific origins)\n */\n moderate: (allowedOrigins: string[]): CORSConfig => ({\n origin: allowedOrigins,\n methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],\n allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],\n exposedHeaders: ['X-Total-Count'],\n credentials: true,\n maxAge: 86400,\n }),\n\n /**\n * Permissive CORS (all origins) — development only.\n * Logs a warning if used when NODE_ENV === 'production'.\n */\n permissive: (): CORSConfig => {\n if (process.env.NODE_ENV === 'production') {\n getSecurityLogger().warn(\n '[SecurityPresets] CORS permissive preset used in production — this allows all origins. Use moderate() with explicit origins instead.',\n );\n }\n return {\n origin: '*',\n methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],\n allowedHeaders: ['*'],\n credentials: false,\n maxAge: 86400,\n };\n },\n\n /**\n * API CORS (public read-only APIs) — credentials disabled.\n * Logs a warning if used when NODE_ENV === 'production'.\n */\n api: (): CORSConfig => {\n if (process.env.NODE_ENV === 'production') {\n getSecurityLogger().warn(\n '[SecurityPresets] CORS api preset uses origin:\"*\". For production, pass explicit origins to moderate() instead.',\n );\n }\n return {\n origin: '*',\n methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],\n allowedHeaders: ['Content-Type', 'Authorization', 'X-API-Key'],\n exposedHeaders: ['X-RateLimit-Limit', 'X-RateLimit-Remaining', 'X-RateLimit-Reset'],\n credentials: false,\n maxAge: 86400,\n };\n },\n};\n\n/**\n * Security middleware creator\n */\nexport function createSecurityMiddleware(\n securityConfig?: SecurityHeadersConfig,\n corsConfig?: CORSConfig,\n) {\n const security = new SecurityHeaders(securityConfig);\n const cors = new CORSManager(corsConfig);\n\n return async (request: Request, next: () => Promise<Response>): Promise<Response> => {\n const origin = request.headers.get('Origin');\n\n // Handle CORS preflight\n if (origin && request.method === 'OPTIONS') {\n const preflightResponse = cors.handleRequest(request);\n if (preflightResponse) {\n return preflightResponse;\n }\n }\n\n // Process request\n const response = await next();\n\n // Apply security headers\n security.applyHeaders(response);\n\n // Apply CORS headers\n if (origin) {\n cors.applyHeaders(response, origin);\n }\n\n return response;\n };\n}\n\n/**\n * Rate limiting headers\n */\nexport function setRateLimitHeaders(\n response: Response,\n limit: number,\n remaining: number,\n reset: number,\n): void {\n response.headers.set('X-RateLimit-Limit', limit.toString());\n response.headers.set('X-RateLimit-Remaining', remaining.toString());\n response.headers.set('X-RateLimit-Reset', reset.toString());\n}\n"],"mappings":"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","names":["audit","crypto","crypto","encryption","createHmac","createHmac"]}