mitigator 1.0.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/dist/index.cjs ADDED
@@ -0,0 +1,1643 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ auth: () => auth_exports,
34
+ crypto: () => crypto_exports,
35
+ fs: () => fs_exports,
36
+ headers: () => headers_exports,
37
+ http: () => http_exports,
38
+ presets: () => presets_exports,
39
+ rateLimit: () => rate_limit_exports,
40
+ safeJson: () => safe_json_exports,
41
+ safeMerge: () => safe_merge_exports,
42
+ sanitize: () => sanitize_exports,
43
+ telemetry: () => telemetry_exports,
44
+ utils: () => utils_exports,
45
+ validate: () => validate_exports
46
+ });
47
+ module.exports = __toCommonJS(index_exports);
48
+
49
+ // node_modules/tsup/assets/cjs_shims.js
50
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
51
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
52
+
53
+ // src/sanitize/index.ts
54
+ var sanitize_exports = {};
55
+ __export(sanitize_exports, {
56
+ ALLOWLIST: () => ALLOWLIST,
57
+ escapeHtml: () => escapeHtml,
58
+ isSafeUrl: () => isSafeUrl,
59
+ preventDOMClobbering: () => preventDOMClobbering,
60
+ sanitizeHtml: () => sanitizeHtml,
61
+ sanitizeMediaTags: () => sanitizeMediaTags,
62
+ stripTags: () => stripTags
63
+ });
64
+ var import_sanitize_html = __toESM(require("sanitize-html"), 1);
65
+ var escapeHtml = (input) => {
66
+ const map = {
67
+ "&": "&",
68
+ "<": "&lt;",
69
+ ">": "&gt;",
70
+ '"': "&quot;",
71
+ "'": "&#39;"
72
+ };
73
+ return input.replaceAll(/[&<>"']/g, (m) => map[m]);
74
+ };
75
+ var stripTags = (input) => {
76
+ return input.replaceAll(/<[^>]*>?/gm, "");
77
+ };
78
+ var ALLOWLIST = {
79
+ b: [],
80
+ i: [],
81
+ em: [],
82
+ strong: [],
83
+ p: ["style"],
84
+ a: ["href", "title", "target"],
85
+ ul: [],
86
+ ol: [],
87
+ li: [],
88
+ br: [],
89
+ span: ["style"]
90
+ };
91
+ var sanitizeHtml = (html, rules = ALLOWLIST) => {
92
+ return (0, import_sanitize_html.default)(html, {
93
+ allowedTags: Object.keys(rules),
94
+ allowedAttributes: rules,
95
+ allowedSchemes: ["http", "https", "mailto", "tel"],
96
+ allowProtocolRelative: false,
97
+ enforceHtmlBoundary: true
98
+ });
99
+ };
100
+ var preventDOMClobbering = (html, prefix = "sk-") => {
101
+ const attrRegex = /(\s(id|name)\s*=\s*)(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
102
+ return html.replaceAll(attrRegex, (_match, attrPrefix, _attrName, val1, val2, val3) => {
103
+ const value = val1 || val2 || val3;
104
+ let quote;
105
+ if (val3) {
106
+ quote = "";
107
+ } else if (val1) {
108
+ quote = '"';
109
+ } else {
110
+ quote = "'";
111
+ }
112
+ return `${attrPrefix}${quote}${prefix}${value}${quote}`;
113
+ });
114
+ };
115
+ var sanitizeMediaTags = (html) => {
116
+ const dangerousTags = [
117
+ "script",
118
+ "animate",
119
+ "set",
120
+ "animateMotion",
121
+ "animateTransform",
122
+ "handler",
123
+ "discard",
124
+ "foreignObject"
125
+ ];
126
+ let result = html;
127
+ dangerousTags.forEach((tag) => {
128
+ const regex = new RegExp(String.raw`<${tag}[^>]*>[\s\S]*?<\/${tag}>|<${tag}[^>]*\/>`, "gi");
129
+ result = result.replaceAll(regex, "");
130
+ });
131
+ return result;
132
+ };
133
+ var isSafeUrl = (url) => {
134
+ const allowedProtocols = ["https:", "http:", "mailto:", "tel:"];
135
+ try {
136
+ const parsed = new URL(url);
137
+ return allowedProtocols.includes(parsed.protocol);
138
+ } catch {
139
+ const low = url.trim().toLowerCase();
140
+ return !low.startsWith("javascript:") && !low.startsWith("//") && (low.startsWith("/") || low.startsWith("."));
141
+ }
142
+ };
143
+
144
+ // src/validate/index.ts
145
+ var validate_exports = {};
146
+ __export(validate_exports, {
147
+ SECRET_PATTERNS: () => SECRET_PATTERNS,
148
+ checkPwnedPassword: () => checkPwnedPassword,
149
+ enforceSchema: () => enforceSchema,
150
+ hasInjectionPattern: () => hasInjectionPattern,
151
+ isEmail: () => isEmail,
152
+ isType: () => isType,
153
+ isWeakPassword: () => isWeakPassword,
154
+ scanForSecrets: () => scanForSecrets
155
+ });
156
+ var import_node_crypto = require("crypto");
157
+ var https = __toESM(require("https"), 1);
158
+ var checkPwnedPassword = (password) => {
159
+ return new Promise((resolve2) => {
160
+ const hash = (0, import_node_crypto.createHash)("sha1").update(password).digest("hex").toUpperCase();
161
+ const prefix = hash.slice(0, 5);
162
+ const suffix = hash.slice(5);
163
+ https.get(`https://api.pwnedpasswords.com/range/${prefix}`, (res) => {
164
+ let data = "";
165
+ res.on("data", (chunk) => data += chunk);
166
+ res.on("end", () => {
167
+ const lines = data.split("\n");
168
+ for (const line of lines) {
169
+ const [hashSuffix, count] = line.split(":");
170
+ if (hashSuffix === suffix) {
171
+ return resolve2({ count: Number.parseInt(count.trim()), apiAvailable: true });
172
+ }
173
+ }
174
+ resolve2({ count: 0, apiAvailable: true });
175
+ });
176
+ }).on("error", (err) => {
177
+ console.error("Mitigator: HIBP API connection error.", err);
178
+ resolve2({ count: 0, apiAvailable: false });
179
+ });
180
+ });
181
+ };
182
+ var SECRET_PATTERNS = [
183
+ /AKIA[0-9A-Z]{16}/,
184
+ /-----BEGIN (RSA|OPENSSH|EC|PGP) PRIVATE KEY-----/,
185
+ /ghp_[a-zA-Z0-9]{36}/,
186
+ /sk_live_[a-zA-Z0-9]{24}/
187
+ ];
188
+ var scanForSecrets = (input) => {
189
+ if (typeof input === "string") {
190
+ return SECRET_PATTERNS.some((pattern) => pattern.test(input));
191
+ }
192
+ if (typeof input === "object" && input !== null) {
193
+ return Object.values(input).some(scanForSecrets);
194
+ }
195
+ return false;
196
+ };
197
+ var isWeakPassword = (password) => {
198
+ if (password.length < 8) return true;
199
+ const hasLower = /[a-z]/.test(password);
200
+ const hasUpper = /[A-Z]/.test(password);
201
+ const hasNumber = /\d/.test(password);
202
+ const hasSpecial = /[^a-zA-Z0-9]/.test(password);
203
+ const types = [hasLower, hasUpper, hasNumber, hasSpecial].filter(Boolean).length;
204
+ if (types < 2) return true;
205
+ const common = ["password", "123456", "qwerty", "admin123", "password123", "12345678"];
206
+ if (common.includes(password.toLowerCase())) return true;
207
+ return false;
208
+ };
209
+ var isType = (val, type) => {
210
+ if (type === "array") return Array.isArray(val);
211
+ return typeof val === type && val !== null;
212
+ };
213
+ var enforceSchema = (data, schema) => {
214
+ if (typeof data !== "object" || data === null || Array.isArray(data)) return null;
215
+ const result = {};
216
+ for (const key of Object.keys(schema)) {
217
+ const expectedType = schema[key];
218
+ const value = data[key];
219
+ if (value === void 0 || !isType(value, expectedType)) return null;
220
+ result[key] = value;
221
+ }
222
+ return result;
223
+ };
224
+ var isEmail = (input) => {
225
+ return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(
226
+ input
227
+ );
228
+ };
229
+ var hasInjectionPattern = (input) => {
230
+ if (typeof input !== "string") return false;
231
+ const dangerousPatterns = [
232
+ // SQL Injection
233
+ /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|ALTER|CREATE|EXEC|UNION|ALL|ANY|SOME)\b.*\b(FROM|INTO|SET|TABLE|DATABASE)\b)/i,
234
+ /'\s*OR\s+'?1'?\s*=\s*'?1/i,
235
+ /"\s*OR\s+"?1"?\s*=\s*"?1/i,
236
+ /--\s*$/,
237
+ /;\s*(WAITFOR|DELAY|SLEEP)/i,
238
+ /;\s*(EXEC|EXECUTE)\b/i,
239
+ // NoSQL Injection
240
+ /\$(where|gt|lt|gte|lte|ne|in|nin|regex|expr|eq)/i,
241
+ /\{\s*\$ne\s*:/i,
242
+ // Command Injection
243
+ /(;|\||&&|\|\||`|\$)\s*(cat|ls|pwd|whoami|id|echo|bash|sh|ping|curl|wget)/i
244
+ ];
245
+ return dangerousPatterns.some((pattern) => pattern.test(input));
246
+ };
247
+
248
+ // src/headers/index.ts
249
+ var headers_exports = {};
250
+ __export(headers_exports, {
251
+ buildCSP: () => buildCSP,
252
+ buildStrictCSP: () => buildStrictCSP,
253
+ generateNonce: () => generateNonce,
254
+ parseCSPReport: () => parseCSPReport,
255
+ secureCSP: () => secureCSP,
256
+ standardHeaders: () => standardHeaders
257
+ });
258
+ var import_node_crypto2 = require("crypto");
259
+ var standardHeaders = {
260
+ "Content-Security-Policy": "default-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'; frame-ancestors 'none'; object-src 'none'",
261
+ "X-Frame-Options": "DENY",
262
+ "X-Content-Type-Options": "nosniff",
263
+ "Referrer-Policy": "strict-origin-when-cross-origin",
264
+ "Strict-Transport-Security": "max-age=31536000; includeSubDomains"
265
+ };
266
+ var generateNonce = (length = 16) => {
267
+ return (0, import_node_crypto2.randomBytes)(length).toString("base64");
268
+ };
269
+ var buildCSP = (directives, nonce) => {
270
+ return Object.entries(directives).filter((entry) => {
271
+ const sources = entry[1];
272
+ return Array.isArray(sources) && sources.length > 0;
273
+ }).map(([directive, sources]) => {
274
+ const updatedSources = nonce && (directive === "script-src" || directive === "style-src") ? [...sources, `'nonce-${nonce}'`] : sources;
275
+ return `${directive} ${updatedSources.join(" ")}`;
276
+ }).join("; ");
277
+ };
278
+ var buildStrictCSP = (nonce) => {
279
+ return buildCSP(
280
+ {
281
+ "object-src": ["'none'"],
282
+ "script-src": ["'strict-dynamic'", `'nonce-${nonce}'`, "'unsafe-inline'", "http:", "https:"],
283
+ "base-uri": ["'none'"]
284
+ },
285
+ nonce
286
+ );
287
+ };
288
+ var parseCSPReport = (reportBody) => {
289
+ if (!reportBody) return null;
290
+ const report = reportBody["csp-report"] || (Array.isArray(reportBody) ? reportBody[0] : reportBody);
291
+ if (!report?.["blocked-uri"]) return null;
292
+ return {
293
+ documentUri: report["document-uri"],
294
+ referrer: report["referrer"],
295
+ blockedUri: report["blocked-uri"],
296
+ violatedDirective: report["violated-directive"],
297
+ originalPolicy: report["original-policy"],
298
+ disposition: report["disposition"],
299
+ statusCode: report["status-code"],
300
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
301
+ };
302
+ };
303
+ var secureCSP = buildCSP({
304
+ "default-src": ["'none'"],
305
+ "script-src": ["'self'"],
306
+ "style-src": ["'self'"],
307
+ "img-src": ["'self'"],
308
+ "connect-src": ["'self'"],
309
+ "font-src": ["'self'"],
310
+ "object-src": ["'none'"],
311
+ "base-uri": ["'none'"],
312
+ "form-action": ["'self'"],
313
+ "frame-ancestors": ["'none'"]
314
+ });
315
+
316
+ // src/auth/index.ts
317
+ var auth_exports = {};
318
+ __export(auth_exports, {
319
+ basicAuth: () => basicAuth,
320
+ can: () => can,
321
+ canAccess: () => canAccess,
322
+ generateCSRF: () => generateCSRF,
323
+ generateHmacChallenge: () => generateHmacChallenge,
324
+ generateHoneyToken: () => generateHoneyToken,
325
+ generatePasskeyChallenge: () => generatePasskeyChallenge,
326
+ hasRole: () => hasRole,
327
+ isHoneyToken: () => isHoneyToken,
328
+ isJwtValid: () => isJwtValid,
329
+ parseAuthenticatorData: () => parseAuthenticatorData,
330
+ suggestMFA: () => suggestMFA,
331
+ verifyCSRF: () => verifyCSRF,
332
+ verifyHmacResponse: () => verifyHmacResponse,
333
+ verifyPasskeyAssertion: () => verifyPasskeyAssertion,
334
+ verifyPasskeyRegistration: () => verifyPasskeyRegistration,
335
+ verifyPasskeySignature: () => verifyPasskeySignature
336
+ });
337
+ var import_node_buffer = require("buffer");
338
+ var import_node_crypto3 = require("crypto");
339
+ var basicAuth = (user, pass) => {
340
+ const credentials = user + ":" + pass;
341
+ return "Basic " + import_node_buffer.Buffer.from(credentials).toString("base64");
342
+ };
343
+ var verifyPasskeySignature = (_challenge, response, signature, publicKey, algorithm = "sha256") => {
344
+ try {
345
+ const data = import_node_buffer.Buffer.isBuffer(response) ? response : import_node_buffer.Buffer.from(response);
346
+ const sig = import_node_buffer.Buffer.isBuffer(signature) ? signature : import_node_buffer.Buffer.from(signature, "base64");
347
+ return (0, import_node_crypto3.verify)(algorithm, data, publicKey, sig);
348
+ } catch {
349
+ return false;
350
+ }
351
+ };
352
+ var suggestMFA = (riskScore, lastFingerprint, currentFingerprint) => {
353
+ if (lastFingerprint !== currentFingerprint) return true;
354
+ if (riskScore > 10) return true;
355
+ return false;
356
+ };
357
+ var generateHmacChallenge = (userSalt) => {
358
+ return (0, import_node_crypto3.createHmac)("sha256", userSalt).update((0, import_node_crypto3.randomBytes)(32)).digest("hex");
359
+ };
360
+ var verifyHmacResponse = (challenge, proof, secret) => {
361
+ const expected = (0, import_node_crypto3.createHmac)("sha256", secret).update(challenge).digest("hex");
362
+ const proofBuf = import_node_buffer.Buffer.from(proof, "hex");
363
+ const expectedBuf = import_node_buffer.Buffer.from(expected, "hex");
364
+ if (proofBuf.length !== expectedBuf.length) return false;
365
+ return (0, import_node_crypto3.timingSafeEqual)(proofBuf, expectedBuf);
366
+ };
367
+ var generateHoneyToken = (prefix = "sk") => {
368
+ return `${prefix}_${(0, import_node_crypto3.randomBytes)(16).toString("hex")}_${(0, import_node_crypto3.randomBytes)(8).toString("hex")}`;
369
+ };
370
+ var isHoneyToken = (token, prefix = "sk") => {
371
+ if (!token) return false;
372
+ const parts = token.split("_");
373
+ return parts.length >= 3 && parts[0] === prefix;
374
+ };
375
+ var generateCSRF = () => {
376
+ const token = (0, import_node_crypto3.randomBytes)(32).toString("hex");
377
+ const cookieValue = (0, import_node_crypto3.randomBytes)(32).toString("hex");
378
+ return { token, cookieValue };
379
+ };
380
+ var verifyCSRF = (providedToken, cookieToken) => {
381
+ if (!providedToken || !cookieToken) return false;
382
+ const tokenBuf = import_node_buffer.Buffer.from(providedToken, "hex");
383
+ const cookieBuf = import_node_buffer.Buffer.from(cookieToken, "hex");
384
+ if (tokenBuf.length !== cookieBuf.length) return false;
385
+ return (0, import_node_crypto3.timingSafeEqual)(tokenBuf, cookieBuf);
386
+ };
387
+ var isJwtValid = (token, secretOrPublicKey, algorithm = "HS256") => {
388
+ try {
389
+ const parts = token.split(".");
390
+ if (parts.length !== 3) return false;
391
+ const [headerB64, payloadB64, signatureB64] = parts;
392
+ const header = JSON.parse(import_node_buffer.Buffer.from(headerB64, "base64url").toString("utf8"));
393
+ if (!header.alg || header.alg.toLowerCase() === "none" || header.alg !== algorithm)
394
+ return false;
395
+ const payload = JSON.parse(import_node_buffer.Buffer.from(payloadB64, "base64url").toString("utf8"));
396
+ if (payload.exp && Date.now() >= payload.exp * 1e3) return false;
397
+ const dataToSign = `${headerB64}.${payloadB64}`;
398
+ const signatureBuffer = import_node_buffer.Buffer.from(signatureB64, "base64url");
399
+ if (algorithm.startsWith("HS")) {
400
+ const hashAlg = algorithm.replace("HS", "sha");
401
+ const expectedSignature = (0, import_node_crypto3.createHmac)(hashAlg, secretOrPublicKey).update(dataToSign).digest();
402
+ if (signatureBuffer.length !== expectedSignature.length) return false;
403
+ return (0, import_node_crypto3.timingSafeEqual)(signatureBuffer, expectedSignature);
404
+ } else if (algorithm.startsWith("RS") || algorithm.startsWith("ES")) {
405
+ const hashAlg = algorithm.replace(/RS|ES/, "sha");
406
+ return (0, import_node_crypto3.verify)(hashAlg, import_node_buffer.Buffer.from(dataToSign), secretOrPublicKey, signatureBuffer);
407
+ }
408
+ return false;
409
+ } catch {
410
+ return false;
411
+ }
412
+ };
413
+ var canAccess = (user, resource, rule) => {
414
+ return rule(user, resource);
415
+ };
416
+ var hasRole = (userRoles, requiredRoles) => {
417
+ return requiredRoles.every((role) => userRoles.includes(role));
418
+ };
419
+ var can = (userPermissions, requiredPermission) => {
420
+ return userPermissions.includes(requiredPermission);
421
+ };
422
+ var generatePasskeyChallenge = (length = 32) => {
423
+ return (0, import_node_crypto3.randomBytes)(length).toString("base64url");
424
+ };
425
+ var verifyPasskeyRegistration = (clientDataJSONStr, expectedChallenge, expectedOrigin) => {
426
+ try {
427
+ let jsonStr = clientDataJSONStr;
428
+ try {
429
+ JSON.parse(clientDataJSONStr);
430
+ } catch {
431
+ jsonStr = import_node_buffer.Buffer.from(clientDataJSONStr, "base64url").toString("utf8");
432
+ }
433
+ const clientData = JSON.parse(jsonStr);
434
+ const receivedBytes = import_node_buffer.Buffer.from(clientData.challenge ?? "", "base64url");
435
+ const expectedBytes = import_node_buffer.Buffer.from(expectedChallenge, "base64url");
436
+ const challengeMatches = receivedBytes.length === expectedBytes.length && receivedBytes.length > 0 && (0, import_node_crypto3.timingSafeEqual)(receivedBytes, expectedBytes);
437
+ const typeMatches = clientData.type === "webauthn.create";
438
+ let originMatches = true;
439
+ if (expectedOrigin && clientData.origin !== expectedOrigin) {
440
+ originMatches = false;
441
+ }
442
+ if (!challengeMatches) return { verified: false, error: "Challenge mismatch." };
443
+ if (!typeMatches) return { verified: false, error: "Invalid type (expected webauthn.create)." };
444
+ if (!originMatches) return { verified: false, error: "Origin mismatch." };
445
+ return { verified: true };
446
+ } catch (err) {
447
+ return { verified: false, error: err.message };
448
+ }
449
+ };
450
+ var parseAuthenticatorData = (authData) => {
451
+ try {
452
+ const rpIdHash = authData.subarray(0, 32);
453
+ const flags = authData[32];
454
+ const signCount = authData.readUInt32BE(33);
455
+ const attestedCredentialDataPresent = !!(flags & 64);
456
+ if (!attestedCredentialDataPresent) {
457
+ return { rpIdHash, flags, signCount };
458
+ }
459
+ const aaguid = authData.subarray(37, 53);
460
+ const credentialIdLength = authData.readUInt16BE(53);
461
+ const credentialId = authData.subarray(55, 55 + credentialIdLength);
462
+ const publicKeyBytes = authData.subarray(55 + credentialIdLength);
463
+ return {
464
+ rpIdHash,
465
+ flags,
466
+ signCount,
467
+ aaguid,
468
+ credentialId: credentialId.toString("base64url"),
469
+ publicKeyBytes: publicKeyBytes.toString("hex")
470
+ };
471
+ } catch {
472
+ return null;
473
+ }
474
+ };
475
+ var verifyPasskeyAssertion = (clientDataJSONStr, expectedChallenge, expectedOrigin, storedSignCount, authData, signature, publicKeyPem) => {
476
+ try {
477
+ let jsonStr = clientDataJSONStr;
478
+ try {
479
+ JSON.parse(clientDataJSONStr);
480
+ } catch {
481
+ jsonStr = import_node_buffer.Buffer.from(clientDataJSONStr, "base64url").toString("utf8");
482
+ }
483
+ const clientData = JSON.parse(jsonStr);
484
+ if (clientData.type !== "webauthn.get") {
485
+ return { verified: false, error: "Invalid type (expected webauthn.get)." };
486
+ }
487
+ const receivedBytes = import_node_buffer.Buffer.from(clientData.challenge ?? "", "base64url");
488
+ const expectedBytes = import_node_buffer.Buffer.from(expectedChallenge, "base64url");
489
+ const challengeMatches = receivedBytes.length === expectedBytes.length && receivedBytes.length > 0 && (0, import_node_crypto3.timingSafeEqual)(receivedBytes, expectedBytes);
490
+ if (!challengeMatches) return { verified: false, error: "Challenge mismatch." };
491
+ if (clientData.origin !== expectedOrigin) {
492
+ return { verified: false, error: "Origin mismatch." };
493
+ }
494
+ const newSignCount = authData.readUInt32BE(33);
495
+ if (storedSignCount !== 0 && newSignCount <= storedSignCount) {
496
+ return {
497
+ verified: false,
498
+ error: `Replay attack or cloned authenticator detected: signCount ${newSignCount} is not greater than stored ${storedSignCount}.`
499
+ };
500
+ }
501
+ const clientDataHash = (0, import_node_crypto3.createHash)("sha256").update(import_node_buffer.Buffer.from(jsonStr, "utf8")).digest();
502
+ const signedData = import_node_buffer.Buffer.concat([authData, clientDataHash]);
503
+ const sigValid = (0, import_node_crypto3.verify)("sha256", signedData, publicKeyPem, signature);
504
+ if (!sigValid) return { verified: false, error: "Signature verification failed." };
505
+ return { verified: true, newSignCount };
506
+ } catch (err) {
507
+ return { verified: false, error: err.message };
508
+ }
509
+ };
510
+
511
+ // src/crypto/index.ts
512
+ var crypto_exports = {};
513
+ __export(crypto_exports, {
514
+ blindData: () => blindData,
515
+ decryptSession: () => decryptSession,
516
+ deriveSubKey: () => deriveSubKey,
517
+ encryptSession: () => encryptSession,
518
+ generatePQCKeyPair: () => generatePQCKeyPair,
519
+ generateToken: () => generateToken,
520
+ hashPassword: () => hashPassword,
521
+ reconstructSecret: () => reconstructSecret,
522
+ runIsolatedCrypto: () => runIsolatedCrypto,
523
+ sha256: () => sha256,
524
+ signPQC: () => signPQC,
525
+ splitSecret: () => splitSecret,
526
+ verifyPQCSignature: () => verifyPQCSignature,
527
+ verifyPassword: () => verifyPassword
528
+ });
529
+ var import_node_buffer2 = require("buffer");
530
+ var import_node_crypto4 = require("crypto");
531
+ var import_node_util = require("util");
532
+ var import_node_url = require("url");
533
+ var import_node_os = require("os");
534
+
535
+ // src/utils/telemetry.ts
536
+ var telemetry_exports = {};
537
+ __export(telemetry_exports, {
538
+ getTelemetryProvider: () => getTelemetryProvider,
539
+ resetTelemetryProvider: () => resetTelemetryProvider,
540
+ setTelemetryProvider: () => setTelemetryProvider
541
+ });
542
+ var NOOP_PROVIDER = {
543
+ onRateLimitBlocked: () => {
544
+ },
545
+ onSecurityEvent: () => {
546
+ },
547
+ onKillSwitchTriggered: () => {
548
+ },
549
+ onWorkerSpawned: () => {
550
+ }
551
+ };
552
+ var _provider = NOOP_PROVIDER;
553
+ var setTelemetryProvider = (provider) => {
554
+ _provider = provider;
555
+ };
556
+ var resetTelemetryProvider = () => {
557
+ _provider = NOOP_PROVIDER;
558
+ };
559
+ var getTelemetryProvider = () => _provider;
560
+
561
+ // src/crypto/index.ts
562
+ var scryptAsync = (0, import_node_util.promisify)(import_node_crypto4.scrypt);
563
+ var getFilename = () => {
564
+ return (0, import_node_url.fileURLToPath)(importMetaUrl);
565
+ };
566
+ var BoundedWorkerSemaphore = class {
567
+ constructor(maxConcurrent = Math.max(2, (0, import_node_os.cpus)().length - 1)) {
568
+ this.maxConcurrent = maxConcurrent;
569
+ }
570
+ maxConcurrent;
571
+ active = 0;
572
+ queue = [];
573
+ async run(task) {
574
+ if (this.active >= this.maxConcurrent) {
575
+ await new Promise((resolve2) => this.queue.push(resolve2));
576
+ }
577
+ this.active++;
578
+ try {
579
+ return await task();
580
+ } finally {
581
+ this.active--;
582
+ const next = this.queue.shift();
583
+ if (next) next();
584
+ }
585
+ }
586
+ };
587
+ var workerPool = new BoundedWorkerSemaphore();
588
+ var runIsolatedCrypto = async (functionName, args) => {
589
+ return workerPool.run(async () => {
590
+ getTelemetryProvider().onWorkerSpawned?.(functionName);
591
+ const { Worker } = await import("worker_threads");
592
+ return new Promise((resolve2, reject) => {
593
+ const worker = new Worker(getFilename(), {
594
+ workerData: { functionName, args }
595
+ });
596
+ worker.on("message", (msg) => {
597
+ if (msg && msg.status === "success") {
598
+ resolve2(msg.result !== void 0 ? msg.result : msg);
599
+ } else {
600
+ reject(new Error(msg?.error || "Worker execution failed."));
601
+ }
602
+ });
603
+ worker.on("error", reject);
604
+ worker.on("exit", (code) => {
605
+ if (code !== 0) reject(new Error(`Worker stopped with exit code ${code}`));
606
+ });
607
+ });
608
+ });
609
+ };
610
+ var gfExp = new Uint8Array(256);
611
+ var gfLog = new Uint8Array(256);
612
+ var gfX = 1;
613
+ for (let i = 0; i < 255; i++) {
614
+ gfExp[i] = gfX;
615
+ gfLog[gfX] = i;
616
+ gfX = gfX << 1;
617
+ if (gfX & 256) {
618
+ gfX = gfX ^ 285;
619
+ }
620
+ }
621
+ gfExp[255] = gfExp[0];
622
+ var gfMul = (a, b) => {
623
+ if (a === 0 || b === 0) return 0;
624
+ return gfExp[(gfLog[a] + gfLog[b]) % 255];
625
+ };
626
+ var gfDiv = (a, b) => {
627
+ if (a === 0) return 0;
628
+ if (b === 0) throw new Error("Division by zero in GF(256)");
629
+ return gfExp[(gfLog[a] - gfLog[b] + 255) % 255];
630
+ };
631
+ var evaluatePolynomial = (coefficients, x) => {
632
+ let result = 0;
633
+ let power = 1;
634
+ for (const coeff of coefficients) {
635
+ result ^= gfMul(coeff, power);
636
+ power = gfMul(power, x);
637
+ }
638
+ return result;
639
+ };
640
+ var splitSecret = (secret, sharesCount, threshold) => {
641
+ if (threshold > sharesCount) throw new Error("Threshold cannot be greater than sharesCount.");
642
+ if (threshold < 1) throw new Error("Threshold must be at least 1.");
643
+ if (sharesCount > 255)
644
+ throw new Error("Shamir Secret Sharing GF(256) supports up to 255 shares.");
645
+ const secretBuffer = import_node_buffer2.Buffer.isBuffer(secret) ? secret : import_node_buffer2.Buffer.from(secret);
646
+ const secretLen = secretBuffer.length;
647
+ const shareBuffers = Array.from({ length: sharesCount }, () => import_node_buffer2.Buffer.alloc(secretLen));
648
+ for (let byteIdx = 0; byteIdx < secretLen; byteIdx++) {
649
+ const s = secretBuffer[byteIdx];
650
+ const coefficients = [s];
651
+ for (let k = 1; k < threshold; k++) {
652
+ coefficients.push((0, import_node_crypto4.randomBytes)(1)[0]);
653
+ }
654
+ for (let i = 0; i < sharesCount; i++) {
655
+ shareBuffers[i][byteIdx] = evaluatePolynomial(coefficients, i + 1);
656
+ }
657
+ }
658
+ return shareBuffers.map((buf, i) => `${i + 1}:${buf.toString("hex")}`);
659
+ };
660
+ var reconstructSecret = (shares) => {
661
+ if (shares.length === 0) throw new Error("No shares provided.");
662
+ const parsedShares = shares.map((s) => {
663
+ const [xStr, hex] = s.split(":");
664
+ return {
665
+ x: Number.parseInt(xStr, 10),
666
+ y: import_node_buffer2.Buffer.from(hex, "hex")
667
+ };
668
+ });
669
+ const secretLen = parsedShares[0].y.length;
670
+ const secretBuffer = import_node_buffer2.Buffer.alloc(secretLen);
671
+ for (let byteIdx = 0; byteIdx < secretLen; byteIdx++) {
672
+ let secretByte = 0;
673
+ for (let i = 0; i < parsedShares.length; i++) {
674
+ let li = 1;
675
+ for (let j = 0; j < parsedShares.length; j++) {
676
+ if (i === j) continue;
677
+ const num = parsedShares[j].x;
678
+ const denom = parsedShares[i].x ^ parsedShares[j].x;
679
+ li = gfMul(li, gfDiv(num, denom));
680
+ }
681
+ secretByte = secretByte ^ gfMul(parsedShares[i].y[byteIdx], li);
682
+ }
683
+ secretBuffer[byteIdx] = secretByte;
684
+ }
685
+ return secretBuffer;
686
+ };
687
+ var blindData = (data, factor) => {
688
+ return (0, import_node_crypto4.createHmac)("sha256", factor).update(data).digest("hex");
689
+ };
690
+ var generateToken = (length = 32, encoding = "hex") => {
691
+ return (0, import_node_crypto4.randomBytes)(length).toString(encoding);
692
+ };
693
+ var deriveSubKey = (secret, info, length = 32) => {
694
+ const ikm = import_node_buffer2.Buffer.isBuffer(secret) ? secret : import_node_buffer2.Buffer.from(secret);
695
+ const derived = (0, import_node_crypto4.hkdfSync)("sha256", ikm, import_node_buffer2.Buffer.alloc(0), import_node_buffer2.Buffer.from(info), length);
696
+ return import_node_buffer2.Buffer.from(derived).toString("hex");
697
+ };
698
+ var encryptSession = (obj, key) => {
699
+ const iv = (0, import_node_crypto4.randomBytes)(12);
700
+ const keyBuffer = import_node_buffer2.Buffer.isBuffer(key) ? key : import_node_buffer2.Buffer.from(key, "hex");
701
+ const cipher = (0, import_node_crypto4.createCipheriv)("aes-256-gcm", keyBuffer, iv);
702
+ const content = JSON.stringify(obj);
703
+ let encrypted = cipher.update(content, "utf8", "hex");
704
+ encrypted += cipher.final("hex");
705
+ const authTag = cipher.getAuthTag().toString("hex");
706
+ return `${iv.toString("hex")}:${authTag}:${encrypted}`;
707
+ };
708
+ var decryptSession = (sessionStr, key) => {
709
+ try {
710
+ const [ivHex, authTagHex, encrypted] = sessionStr.split(":");
711
+ const keyBuffer = import_node_buffer2.Buffer.isBuffer(key) ? key : import_node_buffer2.Buffer.from(key, "hex");
712
+ const iv = import_node_buffer2.Buffer.from(ivHex, "hex");
713
+ const authTag = import_node_buffer2.Buffer.from(authTagHex, "hex");
714
+ const decipher = (0, import_node_crypto4.createDecipheriv)("aes-256-gcm", keyBuffer, iv);
715
+ decipher.setAuthTag(authTag);
716
+ let decrypted = decipher.update(encrypted, "hex", "utf8");
717
+ decrypted += decipher.final("utf8");
718
+ return JSON.parse(decrypted);
719
+ } catch {
720
+ return null;
721
+ }
722
+ };
723
+ var WOTS_LEN = 67;
724
+ var _usedWotsKeys = /* @__PURE__ */ new Set();
725
+ var generatePQCKeyPair = () => {
726
+ const privKeyParts = [];
727
+ const pubKeyParts = [];
728
+ for (let i = 0; i < WOTS_LEN; i++) {
729
+ const part = (0, import_node_crypto4.randomBytes)(32).toString("hex");
730
+ privKeyParts.push(part);
731
+ let current = import_node_buffer2.Buffer.from(part, "hex");
732
+ for (let j = 0; j < 15; j++) {
733
+ current = (0, import_node_crypto4.createHash)("sha256").update(current).digest();
734
+ }
735
+ pubKeyParts.push(current.toString("hex"));
736
+ }
737
+ return {
738
+ publicKey: pubKeyParts.join(";"),
739
+ privateKey: privKeyParts.join(";")
740
+ };
741
+ };
742
+ var signPQC = (data, privateKeyStr) => {
743
+ const msgHash = (0, import_node_crypto4.createHash)("sha256").update(data).digest();
744
+ const nibbles = [];
745
+ let checksum = 0;
746
+ for (let i = 0; i < 32; i++) {
747
+ const byte = msgHash[i];
748
+ const n1 = byte >> 4;
749
+ const n2 = byte & 15;
750
+ nibbles.push(n1, n2);
751
+ checksum += 15 - n1 + (15 - n2);
752
+ }
753
+ const c1 = checksum >> 8 & 15;
754
+ const c2 = checksum >> 4 & 15;
755
+ const c3 = checksum & 15;
756
+ nibbles.push(c1, c2, c3);
757
+ const privParts = privateKeyStr.split(";");
758
+ if (privParts.length !== WOTS_LEN) throw new Error("Invalid private key length.");
759
+ const keyFingerprint = (0, import_node_crypto4.createHash)("sha256").update(privParts[0]).digest("hex");
760
+ if (_usedWotsKeys.has(keyFingerprint)) {
761
+ throw new Error(
762
+ "WOTS_KEY_REUSE: This WOTS private key has already been used to sign a message. Reusing a WOTS key leaks private key material and breaks the security of the scheme. Generate a new key pair with generatePQCKeyPair() for each message."
763
+ );
764
+ }
765
+ _usedWotsKeys.add(keyFingerprint);
766
+ const sigParts = [];
767
+ for (let i = 0; i < WOTS_LEN; i++) {
768
+ const n = nibbles[i];
769
+ let current = import_node_buffer2.Buffer.from(privParts[i], "hex");
770
+ for (let j = 0; j < n; j++) {
771
+ current = (0, import_node_crypto4.createHash)("sha256").update(current).digest();
772
+ }
773
+ sigParts.push(current.toString("hex"));
774
+ }
775
+ return sigParts.join(";");
776
+ };
777
+ var verifyPQCSignature = (data, signature, publicKey) => {
778
+ try {
779
+ const msgHash = (0, import_node_crypto4.createHash)("sha256").update(data).digest();
780
+ const nibbles = [];
781
+ let checksum = 0;
782
+ for (let i = 0; i < 32; i++) {
783
+ const byte = msgHash[i];
784
+ const n1 = byte >> 4;
785
+ const n2 = byte & 15;
786
+ nibbles.push(n1, n2);
787
+ checksum += 15 - n1 + (15 - n2);
788
+ }
789
+ const c1 = checksum >> 8 & 15;
790
+ const c2 = checksum >> 4 & 15;
791
+ const c3 = checksum & 15;
792
+ nibbles.push(c1, c2, c3);
793
+ const sigParts = signature.split(";");
794
+ const pubParts = publicKey.split(";");
795
+ if (sigParts.length !== WOTS_LEN || pubParts.length !== WOTS_LEN) return false;
796
+ for (let i = 0; i < WOTS_LEN; i++) {
797
+ const n = nibbles[i];
798
+ let current = import_node_buffer2.Buffer.from(sigParts[i], "hex");
799
+ for (let j = 0; j < 15 - n; j++) {
800
+ current = (0, import_node_crypto4.createHash)("sha256").update(current).digest();
801
+ }
802
+ const expected = import_node_buffer2.Buffer.from(pubParts[i], "hex");
803
+ if (!(0, import_node_crypto4.timingSafeEqual)(current, expected)) return false;
804
+ }
805
+ return true;
806
+ } catch {
807
+ return false;
808
+ }
809
+ };
810
+ var hashPassword = async (password, salt) => {
811
+ const actualSalt = salt || generateToken(16);
812
+ const derivedKey = await scryptAsync(password, actualSalt, 64);
813
+ return { hash: derivedKey.toString("hex"), salt: actualSalt };
814
+ };
815
+ var verifyPassword = async (password, storedHash, salt) => {
816
+ const { hash } = await hashPassword(password, salt);
817
+ const hashBuffer = import_node_buffer2.Buffer.from(hash, "hex");
818
+ const storedBuffer = import_node_buffer2.Buffer.from(storedHash, "hex");
819
+ if (hashBuffer.length !== storedBuffer.length) return false;
820
+ return (0, import_node_crypto4.timingSafeEqual)(hashBuffer, storedBuffer);
821
+ };
822
+ var sha256 = (data) => {
823
+ return (0, import_node_crypto4.createHash)("sha256").update(data).digest("hex");
824
+ };
825
+ var startWorkerIfChild = async () => {
826
+ try {
827
+ const { isMainThread, parentPort, workerData } = await import("worker_threads");
828
+ if (!isMainThread && parentPort) {
829
+ try {
830
+ const { functionName, args = [] } = workerData || {};
831
+ if (functionName === "hashPassword") {
832
+ const [password, salt] = args;
833
+ if (password === void 0) {
834
+ parentPort.postMessage({ status: "success" });
835
+ } else {
836
+ hashPassword(password, salt).then((result) => {
837
+ parentPort.postMessage({ status: "success", result });
838
+ }).catch((err) => {
839
+ parentPort.postMessage({ status: "error", error: err.message });
840
+ });
841
+ }
842
+ } else if (functionName === "splitSecret") {
843
+ const [secret, sharesCount, threshold] = args;
844
+ const result = splitSecret(secret, sharesCount, threshold);
845
+ parentPort.postMessage({ status: "success", result });
846
+ } else if (functionName === "reconstructSecret") {
847
+ const [shares] = args;
848
+ const result = reconstructSecret(shares);
849
+ parentPort.postMessage({ status: "success", result: result.toString("hex") });
850
+ } else {
851
+ parentPort.postMessage({ status: "error", error: `Unknown function: ${functionName}` });
852
+ }
853
+ } catch (err) {
854
+ parentPort.postMessage({ status: "error", error: err.message });
855
+ }
856
+ }
857
+ } catch {
858
+ }
859
+ };
860
+ startWorkerIfChild();
861
+
862
+ // src/fs/index.ts
863
+ var fs_exports = {};
864
+ __export(fs_exports, {
865
+ MAGIC_NUMBERS: () => MAGIC_NUMBERS,
866
+ isPathSafe: () => isPathSafe,
867
+ resolveSafePath: () => resolveSafePath,
868
+ safeRead: () => safeRead,
869
+ verifyMagicNumber: () => verifyMagicNumber
870
+ });
871
+ var import_node_path = require("path");
872
+ var MAGIC_NUMBERS = {
873
+ PNG: [137, 80, 78, 71],
874
+ JPEG: [255, 216, 255],
875
+ PDF: [37, 80, 68, 70],
876
+ GIF: [71, 73, 70, 56]
877
+ };
878
+ var resolveSafePath = (rootDir, userInputPath) => {
879
+ const absoluteRoot = (0, import_node_path.resolve)(rootDir);
880
+ const resolvedPath = (0, import_node_path.resolve)(absoluteRoot, userInputPath);
881
+ const relativePath = (0, import_node_path.relative)(absoluteRoot, resolvedPath);
882
+ if (relativePath.startsWith("..") || (0, import_node_path.isAbsolute)(relativePath)) {
883
+ throw new Error("Security Error: Path traversal attempt detected.");
884
+ }
885
+ return resolvedPath;
886
+ };
887
+ var verifyMagicNumber = async (filePath, expectedMagic) => {
888
+ const fs = await import("fs/promises");
889
+ const handle = await fs.open(filePath, "r");
890
+ try {
891
+ const buffer = Buffer.alloc(expectedMagic.length);
892
+ await handle.read(buffer, 0, expectedMagic.length, 0);
893
+ return expectedMagic.every((byte, index) => buffer[index] === byte);
894
+ } finally {
895
+ await handle.close();
896
+ }
897
+ };
898
+ var safeRead = async (rootDir, filePath) => {
899
+ const fs = await import("fs/promises");
900
+ const safePath = resolveSafePath(rootDir, filePath);
901
+ return fs.readFile(safePath, "utf8");
902
+ };
903
+ var isPathSafe = (rootDir, filePath) => {
904
+ try {
905
+ resolveSafePath(rootDir, filePath);
906
+ return true;
907
+ } catch {
908
+ return false;
909
+ }
910
+ };
911
+
912
+ // src/http/index.ts
913
+ var http_exports = {};
914
+ __export(http_exports, {
915
+ analyzeDoSThreat: () => analyzeDoSThreat,
916
+ generateFingerprint: () => generateFingerprint,
917
+ generateSRI: () => generateSRI,
918
+ generateTLSFingerprint: () => generateTLSFingerprint,
919
+ getHardenedRequestOptions: () => getHardenedRequestOptions,
920
+ isSafeRedirect: () => isSafeRedirect,
921
+ isSecure: () => isSecure,
922
+ normalizeUrl: () => normalizeUrl,
923
+ projectNoise: () => projectNoise
924
+ });
925
+ var import_node_crypto5 = require("crypto");
926
+ var isSecure = (url, headers = {}) => {
927
+ if (url?.startsWith("https://")) return true;
928
+ const proto = headers["x-forwarded-proto"];
929
+ if (typeof proto === "string" && proto.toLowerCase() === "https") return true;
930
+ return false;
931
+ };
932
+ var normalizeUrl = (url) => {
933
+ try {
934
+ const parsed = new URL(url);
935
+ parsed.pathname = parsed.pathname.replaceAll("//", "/");
936
+ return parsed.toString();
937
+ } catch {
938
+ return url.replaceAll("//", "/");
939
+ }
940
+ };
941
+ var getHardenedRequestOptions = (options = {}) => {
942
+ return {
943
+ ...options,
944
+ minVersion: "TLSv1.3",
945
+ ciphers: "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256",
946
+ rejectUnauthorized: true
947
+ };
948
+ };
949
+ var generateTLSFingerprint = (req) => {
950
+ const headers = req.headers || {};
951
+ const fingerprintParts = [
952
+ headers["user-agent"] || "",
953
+ headers["accept"] || "",
954
+ headers["accept-language"] || "",
955
+ headers["accept-encoding"] || "",
956
+ headers["connection"] || "",
957
+ headers["upgrade-insecure-requests"] || ""
958
+ ];
959
+ return (0, import_node_crypto5.createHash)("sha256").update(fingerprintParts.join("|")).digest("hex");
960
+ };
961
+ var analyzeDoSThreat = (req) => {
962
+ const headers = req.headers || {};
963
+ if (headers["transfer-encoding"] && headers["content-length"]) return true;
964
+ if (headers["connection"] === "keep-alive" && !headers["content-length"] && req.method === "POST")
965
+ return true;
966
+ return false;
967
+ };
968
+ var generateFingerprint = (reqHeaders, ip) => {
969
+ const parts = [
970
+ ip,
971
+ reqHeaders["user-agent"] || "unknown",
972
+ reqHeaders["accept-language"] || "unknown",
973
+ reqHeaders["accept-encoding"] || "unknown",
974
+ reqHeaders["sec-ch-ua"] || "unknown",
975
+ reqHeaders["sec-ch-ua-platform"] || "unknown"
976
+ ];
977
+ return (0, import_node_crypto5.createHash)("sha256").update(parts.join("|")).digest("hex");
978
+ };
979
+ var projectNoise = (obj) => {
980
+ if (typeof obj !== "object" || obj === null) return obj;
981
+ const noiseCount = (0, import_node_crypto5.randomInt)(1, 4);
982
+ const result = { ...obj };
983
+ for (let i = 0; i < noiseCount; i++) {
984
+ const noiseKey = `_sk_${(0, import_node_crypto5.randomBytes)(4).toString("hex")}`;
985
+ const noiseValue = (0, import_node_crypto5.randomBytes)(8).toString("base64");
986
+ result[noiseKey] = noiseValue;
987
+ }
988
+ return result;
989
+ };
990
+ var isSafeRedirect = (url, allowedHosts = []) => {
991
+ if (url.startsWith("/")) return !url.startsWith("//");
992
+ try {
993
+ const parsed = new URL(url);
994
+ return allowedHosts.includes(parsed.hostname);
995
+ } catch {
996
+ return false;
997
+ }
998
+ };
999
+ var generateSRI = (data, algorithm = "sha384") => {
1000
+ const hash = (0, import_node_crypto5.createHash)(algorithm).update(data).digest("base64");
1001
+ return `${algorithm}-${hash}`;
1002
+ };
1003
+
1004
+ // src/rate-limit/index.ts
1005
+ var rate_limit_exports = {};
1006
+ __export(rate_limit_exports, {
1007
+ AdaptiveRateLimiter: () => AdaptiveRateLimiter,
1008
+ MemoryStore: () => MemoryStore,
1009
+ RateLimiter: () => RateLimiter,
1010
+ RedisStore: () => RedisStore,
1011
+ TokenBucket: () => TokenBucket
1012
+ });
1013
+ var MemoryStore = class {
1014
+ constructor(windowMs) {
1015
+ this.windowMs = windowMs;
1016
+ this.gcInterval = setInterval(() => this.gc(), 6e4);
1017
+ if (this.gcInterval.unref) {
1018
+ this.gcInterval.unref();
1019
+ }
1020
+ }
1021
+ windowMs;
1022
+ hits = /* @__PURE__ */ new Map();
1023
+ gcInterval;
1024
+ gc() {
1025
+ const now = Date.now();
1026
+ for (const [key, hit] of this.hits.entries()) {
1027
+ if (now > hit.expires) {
1028
+ this.hits.delete(key);
1029
+ }
1030
+ }
1031
+ }
1032
+ async increment(key, amount = 1) {
1033
+ const now = Date.now();
1034
+ const hit = this.hits.get(key);
1035
+ if (!hit || now > hit.expires) {
1036
+ this.hits.set(key, { count: amount, expires: now + this.windowMs });
1037
+ return amount;
1038
+ }
1039
+ hit.count += amount;
1040
+ return hit.count;
1041
+ }
1042
+ async get(key) {
1043
+ const hit = this.hits.get(key);
1044
+ if (!hit || Date.now() > hit.expires) return void 0;
1045
+ return hit.count;
1046
+ }
1047
+ async reset(key) {
1048
+ this.hits.delete(key);
1049
+ }
1050
+ async setTTL(key, value, ttlMs) {
1051
+ this.hits.set(key, { count: value, expires: Date.now() + ttlMs });
1052
+ }
1053
+ // Cleanup method if needed
1054
+ destroy() {
1055
+ clearInterval(this.gcInterval);
1056
+ }
1057
+ };
1058
+ var RedisStore = class {
1059
+ constructor(redisClient, windowMs, prefix = "rl:") {
1060
+ this.redisClient = redisClient;
1061
+ this.windowMs = windowMs;
1062
+ this.prefix = prefix;
1063
+ }
1064
+ redisClient;
1065
+ windowMs;
1066
+ prefix;
1067
+ async increment(key, amount = 1) {
1068
+ const prefixedKey = this.prefix + key;
1069
+ const multi = this.redisClient.multi();
1070
+ multi.incrby(prefixedKey, amount);
1071
+ multi.pttl(prefixedKey);
1072
+ const results = await multi.exec();
1073
+ const count = Number.parseInt(results[0][1], 10);
1074
+ const ttl = Number.parseInt(results[1][1], 10);
1075
+ if (ttl === -1) {
1076
+ await this.redisClient.pexpire(prefixedKey, this.windowMs);
1077
+ }
1078
+ return count;
1079
+ }
1080
+ async get(key) {
1081
+ const val = await this.redisClient.get(this.prefix + key);
1082
+ return val ? Number.parseInt(val, 10) : void 0;
1083
+ }
1084
+ async reset(key) {
1085
+ await this.redisClient.del(this.prefix + key);
1086
+ }
1087
+ async setTTL(key, value, ttlMs) {
1088
+ const prefixedKey = this.prefix + key;
1089
+ await this.redisClient.set(prefixedKey, value, "PX", ttlMs);
1090
+ }
1091
+ };
1092
+ var TokenBucket = class {
1093
+ constructor(capacity, refillRatePerSecond) {
1094
+ this.capacity = capacity;
1095
+ this.refillRatePerSecond = refillRatePerSecond;
1096
+ }
1097
+ capacity;
1098
+ refillRatePerSecond;
1099
+ buckets = /* @__PURE__ */ new Map();
1100
+ async consume(id, tokens = 1) {
1101
+ const now = Date.now();
1102
+ const bucket = this.buckets.get(id);
1103
+ if (bucket) {
1104
+ const delta = (now - bucket.lastRefill) / 1e3;
1105
+ const refill = delta * this.refillRatePerSecond;
1106
+ bucket.tokens = Math.min(this.capacity, bucket.tokens + refill);
1107
+ bucket.lastRefill = now;
1108
+ } else {
1109
+ const newBucket = { tokens: this.capacity, lastRefill: now };
1110
+ if (newBucket.tokens >= tokens) {
1111
+ newBucket.tokens -= tokens;
1112
+ this.buckets.set(id, newBucket);
1113
+ return true;
1114
+ }
1115
+ this.buckets.set(id, newBucket);
1116
+ return false;
1117
+ }
1118
+ if (bucket.tokens >= tokens) {
1119
+ bucket.tokens -= tokens;
1120
+ this.buckets.set(id, bucket);
1121
+ return true;
1122
+ }
1123
+ this.buckets.set(id, bucket);
1124
+ return false;
1125
+ }
1126
+ };
1127
+ var AdaptiveRateLimiter = class {
1128
+ constructor(config, store, securityStore, burstStore) {
1129
+ this.config = config;
1130
+ this.store = store || new MemoryStore(config.windowMs);
1131
+ this.securityStore = securityStore || new MemoryStore(config.windowMs);
1132
+ this.burstStore = burstStore || new MemoryStore(config.windowMs * 10);
1133
+ }
1134
+ config;
1135
+ store;
1136
+ securityStore;
1137
+ burstStore;
1138
+ ipCheckers = [];
1139
+ /**
1140
+ * Automates Incident Response by globally "killing" a session or IP.
1141
+ * This sets an infinite (effectively 1-year) block on the ID.
1142
+ *
1143
+ * @param id The client ID to block.
1144
+ *
1145
+ * @remarks
1146
+ * **Persistence warning**: If the underlying store is `MemoryStore` (the default),
1147
+ * the block is held in process memory and will be **lost on process restart**.
1148
+ * For a persistent kill-switch that survives restarts and works across multiple
1149
+ * instances, pass a `RedisStore` as the `store` and `securityStore` arguments
1150
+ * to the `AdaptiveRateLimiter` constructor.
1151
+ *
1152
+ * @see {@link RedisStore} for a production-grade persistent store.
1153
+ */
1154
+ async triggerGlobalKillSwitch(id) {
1155
+ getTelemetryProvider().onKillSwitchTriggered?.(id);
1156
+ const ONE_YEAR_MS = 365 * 24 * 60 * 60 * 1e3;
1157
+ await this.securityStore.setTTL(`${id}:score`, 99999, ONE_YEAR_MS);
1158
+ await this.store.setTTL(id, 99999, ONE_YEAR_MS);
1159
+ console.warn(`Mitigator: Global Kill-Switch triggered for actor: ${id}`);
1160
+ }
1161
+ registerIPChecker(checker) {
1162
+ this.ipCheckers.push(checker);
1163
+ }
1164
+ async recordSecurityEvent(id, weight = 1) {
1165
+ getTelemetryProvider().onSecurityEvent?.(id, weight);
1166
+ await this.securityStore.increment(`${id}:score`, weight);
1167
+ }
1168
+ async isLimited(id) {
1169
+ for (const checker of this.ipCheckers) {
1170
+ if (await checker(id)) return true;
1171
+ }
1172
+ const score = await this.securityStore.get(`${id}:score`) || 0;
1173
+ const bursts = await this.burstStore.get(`${id}:bursts`) || 0;
1174
+ const isCompromised = score >= this.config.securityThreshold || bursts >= this.config.burstThreshold;
1175
+ const currentLimit = isCompromised ? this.config.penaltyLimit : this.config.standardLimit;
1176
+ const count = await this.store.increment(id);
1177
+ if (count > currentLimit) {
1178
+ if (count === currentLimit + 1) {
1179
+ await this.burstStore.increment(`${id}:bursts`, 1);
1180
+ }
1181
+ getTelemetryProvider().onRateLimitBlocked?.(id, isCompromised ? "penalty" : "standard");
1182
+ return true;
1183
+ }
1184
+ return false;
1185
+ }
1186
+ async isHighRisk(id) {
1187
+ const score = await this.securityStore.get(`${id}:score`) || 0;
1188
+ const bursts = await this.burstStore.get(`${id}:bursts`) || 0;
1189
+ return score >= this.config.securityThreshold || bursts >= this.config.burstThreshold;
1190
+ }
1191
+ };
1192
+ var RateLimiter = class {
1193
+ constructor(limit, windowMs, store) {
1194
+ this.limit = limit;
1195
+ this.windowMs = windowMs;
1196
+ this.store = store || new MemoryStore(windowMs);
1197
+ }
1198
+ limit;
1199
+ windowMs;
1200
+ store;
1201
+ async isLimited(id) {
1202
+ const count = await this.store.increment(id);
1203
+ return count > this.limit;
1204
+ }
1205
+ };
1206
+
1207
+ // src/safe-json/index.ts
1208
+ var safe_json_exports = {};
1209
+ __export(safe_json_exports, {
1210
+ containsPollution: () => containsPollution,
1211
+ getDepth: () => getDepth,
1212
+ parse: () => parse
1213
+ });
1214
+ var parse = (text, maxDepth = 10) => {
1215
+ const obj = JSON.parse(text, (key, value) => {
1216
+ if (key === "__proto__" || key === "constructor" || key === "prototype") {
1217
+ return void 0;
1218
+ }
1219
+ return value;
1220
+ });
1221
+ if (getDepth(obj) > maxDepth) {
1222
+ throw new Error("Security Error: JSON depth limit exceeded (DoS protection).");
1223
+ }
1224
+ return obj;
1225
+ };
1226
+ var getDepth = (obj) => {
1227
+ if (obj === null || typeof obj !== "object") return 0;
1228
+ let max = 0;
1229
+ for (const key in obj) {
1230
+ if (Object.hasOwn(obj, key)) {
1231
+ max = Math.max(max, getDepth(obj[key]));
1232
+ }
1233
+ }
1234
+ return 1 + max;
1235
+ };
1236
+ var containsPollution = (obj) => {
1237
+ if (obj === null || typeof obj !== "object") return false;
1238
+ for (const key in obj) {
1239
+ if (key === "__proto__" || key === "constructor" || key === "prototype") {
1240
+ return true;
1241
+ }
1242
+ if (containsPollution(obj[key])) {
1243
+ return true;
1244
+ }
1245
+ }
1246
+ return false;
1247
+ };
1248
+
1249
+ // src/safe-merge/index.ts
1250
+ var safe_merge_exports = {};
1251
+ __export(safe_merge_exports, {
1252
+ deepFreeze: () => deepFreeze,
1253
+ merge: () => merge,
1254
+ sanitizeObject: () => sanitizeObject
1255
+ });
1256
+ var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1257
+ var isObject = (val) => {
1258
+ return val !== null && typeof val === "object" && !Array.isArray(val);
1259
+ };
1260
+ var merge = (target, source, seen = /* @__PURE__ */ new WeakSet()) => {
1261
+ if (seen.has(source)) {
1262
+ throw new Error(
1263
+ "Security Error: Circular reference detected during deep merge (DoS prevention)."
1264
+ );
1265
+ }
1266
+ const output = { ...target };
1267
+ if (isObject(target) && isObject(source)) {
1268
+ seen.add(source);
1269
+ Object.keys(source).forEach((key) => {
1270
+ if (DANGEROUS_KEYS.has(key)) {
1271
+ return;
1272
+ }
1273
+ const sourceValue = source[key];
1274
+ const targetValue = target[key];
1275
+ if (isObject(sourceValue) && isObject(targetValue)) {
1276
+ output[key] = merge(targetValue, sourceValue, seen);
1277
+ } else {
1278
+ output[key] = sourceValue;
1279
+ }
1280
+ });
1281
+ }
1282
+ return output;
1283
+ };
1284
+ var sanitizeObject = (obj, seen = /* @__PURE__ */ new WeakSet()) => {
1285
+ if (obj === null || typeof obj !== "object") return obj;
1286
+ if (seen.has(obj)) {
1287
+ throw new Error("Security Error: Circular reference detected during object sanitization.");
1288
+ }
1289
+ if (Array.isArray(obj)) {
1290
+ seen.add(obj);
1291
+ return obj.map((item) => sanitizeObject(item, seen));
1292
+ }
1293
+ seen.add(obj);
1294
+ const result = {};
1295
+ for (const [key, value] of Object.entries(obj)) {
1296
+ if (DANGEROUS_KEYS.has(key)) continue;
1297
+ result[key] = sanitizeObject(value, seen);
1298
+ }
1299
+ return result;
1300
+ };
1301
+ var deepFreeze = (obj) => {
1302
+ Object.getOwnPropertyNames(obj).forEach((name) => {
1303
+ const prop = obj[name];
1304
+ if (prop !== null && typeof prop === "object") {
1305
+ deepFreeze(prop);
1306
+ }
1307
+ });
1308
+ return Object.freeze(obj);
1309
+ };
1310
+
1311
+ // src/utils/index.ts
1312
+ var utils_exports = {};
1313
+ __export(utils_exports, {
1314
+ SENSITIVE_KEYS: () => SENSITIVE_KEYS,
1315
+ SecureError: () => SecureError,
1316
+ auditConfig: () => auditConfig,
1317
+ checkSecureEnv: () => checkSecureEnv,
1318
+ createMTLSOptions: () => createMTLSOptions,
1319
+ deterministicDigitTransform: () => deterministicDigitTransform,
1320
+ enforceSafeQuery: () => enforceSafeQuery,
1321
+ lockdownPrototypes: () => lockdownPrototypes,
1322
+ normalizeError: () => normalizeError,
1323
+ redact: () => redact,
1324
+ standardSecurityPreset: () => standardSecurityPreset,
1325
+ startSelfHealingMonitor: () => startSelfHealingMonitor,
1326
+ wipeBuffer: () => wipeBuffer
1327
+ });
1328
+ var import_node_crypto6 = require("crypto");
1329
+ var SENSITIVE_KEYS = /* @__PURE__ */ new Set([
1330
+ "password",
1331
+ "token",
1332
+ "secret",
1333
+ "key",
1334
+ "authorization",
1335
+ "apiKey",
1336
+ "access_token",
1337
+ "refresh_token",
1338
+ "cvv",
1339
+ "card_number",
1340
+ "ssn"
1341
+ ]);
1342
+ var redact = (obj, redactKeys = SENSITIVE_KEYS) => {
1343
+ if (obj === null || typeof obj !== "object") return obj;
1344
+ if (Array.isArray(obj)) return obj.map((item) => redact(item, redactKeys));
1345
+ const result = {};
1346
+ for (const [key, value] of Object.entries(obj)) {
1347
+ if (redactKeys.has(key.toLowerCase())) {
1348
+ result[key] = "[REDACTED]";
1349
+ } else if (typeof value === "object") {
1350
+ result[key] = redact(value, redactKeys);
1351
+ } else {
1352
+ result[key] = value;
1353
+ }
1354
+ }
1355
+ return result;
1356
+ };
1357
+ var SecureError = class extends Error {
1358
+ code;
1359
+ userMessage;
1360
+ constructor(message, code = "INTERNAL_ERROR", userMessage = "An unexpected error occurred.") {
1361
+ super(message);
1362
+ this.name = "SecureError";
1363
+ this.code = code;
1364
+ this.userMessage = userMessage;
1365
+ if (process.env.NODE_ENV === "production") this.stack = "";
1366
+ }
1367
+ toJSON() {
1368
+ return { error: true, code: this.code, message: this.userMessage };
1369
+ }
1370
+ };
1371
+ var normalizeError = (error) => {
1372
+ if (error instanceof SecureError) return error;
1373
+ return new SecureError(error?.message || "Unknown error");
1374
+ };
1375
+ var wipeBuffer = (buf) => {
1376
+ if (Array.isArray(buf)) {
1377
+ for (let i = 0; i < buf.length; i++) buf[i] = 0;
1378
+ } else if (buf instanceof Buffer || buf instanceof Uint8Array) {
1379
+ buf.fill(0);
1380
+ }
1381
+ };
1382
+ var startSelfHealingMonitor = (config, type, intervalMs = 6e5, logger = console) => {
1383
+ setInterval(() => {
1384
+ const issues = auditConfig(config, type);
1385
+ if (issues.length > 0) {
1386
+ logger.error("MITIGATOR CRITICAL: Self-healing monitor detected insecure config drift!");
1387
+ issues.forEach((issue) => logger.error(`- ${issue}`));
1388
+ }
1389
+ }, intervalMs).unref();
1390
+ };
1391
+ var enforceSafeQuery = (query, params) => {
1392
+ if ((!params || params.length === 0) && query.toLowerCase().includes("where")) {
1393
+ const suspicious = ["'", '"', "=", "--", ";"];
1394
+ if (suspicious.some((char) => query.includes(char))) {
1395
+ throw new SecureError(
1396
+ "Security Violation: Unparameterized query detected.",
1397
+ "SQL_INJECTION_RISK"
1398
+ );
1399
+ }
1400
+ }
1401
+ };
1402
+ var deterministicDigitTransform = (input, secret) => {
1403
+ const hash = (0, import_node_crypto6.createHash)("sha256").update(input).update(secret).digest("hex");
1404
+ let result = "";
1405
+ for (let i = 0; i < input.length; i++) {
1406
+ const digit = Number.parseInt(input[i]);
1407
+ const offset = Number.parseInt(hash[i % hash.length], 16);
1408
+ result += ((digit + offset) % 10).toString();
1409
+ }
1410
+ return result;
1411
+ };
1412
+ var checkSecureEnv = () => {
1413
+ const isProd = process.env.NODE_ENV === "production";
1414
+ if (isProd) {
1415
+ if (!process.env.SESSION_SECRET || process.env.SESSION_SECRET.length < 32) {
1416
+ console.warn("Security Warning: SESSION_SECRET is missing or too short.");
1417
+ }
1418
+ }
1419
+ };
1420
+ var auditConfig = (config, type) => {
1421
+ const issues = [];
1422
+ if (type === "db") {
1423
+ if (config.ssl === false) issues.push("Database SSL is disabled.");
1424
+ if (config.port === 3306 || config.port === 5432)
1425
+ issues.push("Database is using a default port.");
1426
+ } else if (type === "auth") {
1427
+ if (config.allowInsecurePasswordReset) issues.push("Allowing insecure password reset.");
1428
+ }
1429
+ return issues;
1430
+ };
1431
+ var lockdownPrototypes = () => {
1432
+ try {
1433
+ Object.freeze(Object.prototype);
1434
+ Object.freeze(Array.prototype);
1435
+ Object.freeze(String.prototype);
1436
+ } catch {
1437
+ console.error("Mitigator Error: Failed to lockdown prototypes.");
1438
+ }
1439
+ };
1440
+ var createMTLSOptions = (serverCert, serverKey, caCert) => {
1441
+ return {
1442
+ cert: serverCert,
1443
+ key: serverKey,
1444
+ ca: caCert,
1445
+ requestCert: true,
1446
+ rejectUnauthorized: true
1447
+ };
1448
+ };
1449
+ var standardSecurityPreset = {
1450
+ trustProxy: true,
1451
+ strictTransportSecurity: { maxAge: 31536e3, includeSubDomains: true, preload: true },
1452
+ contentSecurityPolicy: true,
1453
+ referrerPolicy: "strict-origin-when-cross-origin"
1454
+ };
1455
+
1456
+ // src/utils/presets.ts
1457
+ var presets_exports = {};
1458
+ __export(presets_exports, {
1459
+ NestJsMitigatorMiddleware: () => NestJsMitigatorMiddleware,
1460
+ SecureLoggerChain: () => SecureLoggerChain,
1461
+ createSecureLogger: () => createSecureLogger,
1462
+ expressErrorHandler: () => expressErrorHandler,
1463
+ expressMiddleware: () => expressMiddleware,
1464
+ fastifyPlugin: () => fastifyPlugin,
1465
+ nextJsMiddleware: () => nextJsMiddleware
1466
+ });
1467
+ var import_node_crypto7 = require("crypto");
1468
+ var expressMiddleware = (options = {}) => {
1469
+ const limiter = options.rateLimit ? new RateLimiter(
1470
+ options.rateLimitMax || 100,
1471
+ options.rateLimitWindowMs || 6e4,
1472
+ new MemoryStore(options.rateLimitWindowMs || 6e4)
1473
+ ) : null;
1474
+ return async (req, res, next) => {
1475
+ Object.entries(standardHeaders).forEach(([key, value]) => {
1476
+ res.setHeader(key, value);
1477
+ });
1478
+ if (limiter) {
1479
+ const ip = req.ip || req.connection.remoteAddress || "unknown";
1480
+ if (await limiter.isLimited(ip)) {
1481
+ return res.status(429).json({ error: "Too Many Requests" });
1482
+ }
1483
+ }
1484
+ try {
1485
+ if (req.body && typeof req.body === "object") {
1486
+ if (scanForSecrets(req.body)) {
1487
+ return next(
1488
+ new SecureError("Security Violation: Potential secret leakage detected.", "BAD_INPUT")
1489
+ );
1490
+ }
1491
+ }
1492
+ } catch {
1493
+ return next(new SecureError("Security Violation: Suspicious input detected.", "BAD_INPUT"));
1494
+ }
1495
+ req.mitigator = {
1496
+ sanitize: (html) => sanitizeHtml(html),
1497
+ safeJson: (text) => parse(text)
1498
+ };
1499
+ next();
1500
+ };
1501
+ };
1502
+ var nextJsMiddleware = (_req, res) => {
1503
+ Object.entries(standardHeaders).forEach(([key, value]) => {
1504
+ res.headers.set(key, value);
1505
+ });
1506
+ return res;
1507
+ };
1508
+ var expressErrorHandler = (err, _req, res, _next) => {
1509
+ const secureErr = err instanceof SecureError ? err : new SecureError(err.message);
1510
+ res.status(secureErr.code === "BAD_INPUT" ? 400 : 500).json(secureErr.toJSON());
1511
+ };
1512
+ var SecureLoggerChain = class {
1513
+ constructor(baseLogger) {
1514
+ this.baseLogger = baseLogger;
1515
+ this.lastHash = (0, import_node_crypto7.createHash)("sha256").update(Date.now().toString()).digest("hex");
1516
+ }
1517
+ baseLogger;
1518
+ lastHash = "";
1519
+ chain(msg, data) {
1520
+ const payload = JSON.stringify({ msg, data, prev: this.lastHash });
1521
+ this.lastHash = (0, import_node_crypto7.createHash)("sha256").update(payload).digest("hex");
1522
+ return this.lastHash;
1523
+ }
1524
+ info(msg, data) {
1525
+ const hash = this.chain(msg, data);
1526
+ this.baseLogger.info(`[CHAIN:${hash}] ${msg}`, redact(data));
1527
+ }
1528
+ warn(msg, data) {
1529
+ if (scanForSecrets(data)) {
1530
+ this.baseLogger.warn("CRITICAL: Secret leakage blocked!");
1531
+ return;
1532
+ }
1533
+ const hash = this.chain(msg, data);
1534
+ this.baseLogger.warn(`[CHAIN:${hash}] ${msg}`, redact(data));
1535
+ }
1536
+ error(msg, err) {
1537
+ const secureErr = err instanceof SecureError ? err : new SecureError(err?.message || "Error");
1538
+ const hash = this.chain(msg, secureErr.toJSON());
1539
+ this.baseLogger.error(`[CHAIN:${hash}] ${msg}`, redact(secureErr.toJSON()));
1540
+ }
1541
+ };
1542
+ var createSecureLogger = (baseLogger) => new SecureLoggerChain(baseLogger);
1543
+ var fastifyPlugin = (options = {}) => {
1544
+ const limiter = options.rateLimit ? new RateLimiter(
1545
+ options.rateLimitMax || 100,
1546
+ options.rateLimitWindowMs || 6e4,
1547
+ new MemoryStore(options.rateLimitWindowMs || 6e4)
1548
+ ) : null;
1549
+ return async (fastify) => {
1550
+ fastify.addHook("onRequest", async (req, reply) => {
1551
+ Object.entries(standardHeaders).forEach(([key, value]) => {
1552
+ reply.header(key, value);
1553
+ });
1554
+ if (limiter) {
1555
+ const ip = req.ip || "unknown";
1556
+ if (await limiter.isLimited(ip)) {
1557
+ reply.code(429).send({ error: "Too Many Requests" });
1558
+ return reply;
1559
+ }
1560
+ }
1561
+ });
1562
+ fastify.addHook("preHandler", async (req, reply) => {
1563
+ try {
1564
+ if (req.body && typeof req.body === "object") {
1565
+ if (scanForSecrets(req.body)) {
1566
+ throw new SecureError(
1567
+ "Security Violation: Potential secret leakage detected.",
1568
+ "BAD_INPUT"
1569
+ );
1570
+ }
1571
+ }
1572
+ } catch (err) {
1573
+ const secureErr = err instanceof SecureError ? err : new SecureError(err.message, "BAD_INPUT");
1574
+ reply.code(400).send(secureErr.toJSON());
1575
+ return reply;
1576
+ }
1577
+ req.mitigator = {
1578
+ sanitize: (html) => sanitizeHtml(html),
1579
+ safeJson: (text) => parse(text)
1580
+ };
1581
+ });
1582
+ };
1583
+ };
1584
+ var NestJsMitigatorMiddleware = class _NestJsMitigatorMiddleware {
1585
+ static limiter = null;
1586
+ static configure(options = {}) {
1587
+ if (options.rateLimit) {
1588
+ this.limiter = new RateLimiter(
1589
+ options.rateLimitMax || 100,
1590
+ options.rateLimitWindowMs || 6e4,
1591
+ new MemoryStore(options.rateLimitWindowMs || 6e4)
1592
+ );
1593
+ }
1594
+ }
1595
+ async use(req, res, next) {
1596
+ Object.entries(standardHeaders).forEach(([key, value]) => {
1597
+ res.setHeader(key, value);
1598
+ });
1599
+ if (_NestJsMitigatorMiddleware.limiter) {
1600
+ const ip = req.ip || req.connection?.remoteAddress || "unknown";
1601
+ if (await _NestJsMitigatorMiddleware.limiter.isLimited(ip)) {
1602
+ res.status(429).json({ error: "Too Many Requests" });
1603
+ return;
1604
+ }
1605
+ }
1606
+ try {
1607
+ if (req.body && typeof req.body === "object") {
1608
+ if (scanForSecrets(req.body)) {
1609
+ throw new SecureError(
1610
+ "Security Violation: Potential secret leakage detected.",
1611
+ "BAD_INPUT"
1612
+ );
1613
+ }
1614
+ }
1615
+ } catch (err) {
1616
+ const secureErr = err instanceof SecureError ? err : new SecureError(err.message, "BAD_INPUT");
1617
+ res.status(400).json(secureErr.toJSON());
1618
+ return;
1619
+ }
1620
+ req.mitigator = {
1621
+ sanitize: (html) => sanitizeHtml(html),
1622
+ safeJson: (text) => parse(text)
1623
+ };
1624
+ next();
1625
+ }
1626
+ };
1627
+ // Annotate the CommonJS export names for ESM import in node:
1628
+ 0 && (module.exports = {
1629
+ auth,
1630
+ crypto,
1631
+ fs,
1632
+ headers,
1633
+ http,
1634
+ presets,
1635
+ rateLimit,
1636
+ safeJson,
1637
+ safeMerge,
1638
+ sanitize,
1639
+ telemetry,
1640
+ utils,
1641
+ validate
1642
+ });
1643
+ //# sourceMappingURL=index.cjs.map