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/LICENSE.md ADDED
@@ -0,0 +1,15 @@
1
+ # ISC License
2
+
3
+ Copyright (c) 2026, Mohamed
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,355 @@
1
+ # Mitigator 🛡️
2
+
3
+ **Mitigator** is a production-grade, security-first TypeScript library designed to eliminate common vulnerabilities and implement advanced defense-in-depth patterns in Node.js applications.
4
+
5
+ Unlike generic utility libraries, Mitigator is built with a "Zero-Trust" philosophy, providing tools specifically hardened against XSS, Prototype Pollution, Path Traversal, SQL Injection, and more.
6
+
7
+ ## 🛡️ Mitigated Risks
8
+
9
+ Mitigator provides built-in defenses against the most critical web vulnerabilities, mapping directly to OWASP Top 10 categories:
10
+
11
+ - **Cross-Site Scripting (XSS)**: Automatic HTML escaping and allowlist-based sanitization prevent malicious scripts from executing in the user's browser.
12
+ - **Prototype Pollution**: Strict filtering of `__proto__` and `constructor` keys during JSON parsing and object merging prevents attackers from compromising the Node.js runtime.
13
+ - **Path Traversal (LFI)**: Root-locked path resolution ensures that file system operations cannot escape designated directories.
14
+ - **Injection Attacks**: Heuristic detection of SQL/NoSQL injection patterns and enforcement of parameterized-like query structures.
15
+ - **Broken Authentication**: Protection against credential stuffing via HIBP leak checks and support for modern, phishing-resistant WebAuthn (Passkeys).
16
+ - **Denial of Service (DoS)**: Rate limiting, JSON depth analysis, and circular reference detection prevent resource exhaustion attacks.
17
+ - **Sensitive Data Exposure**: Automated redaction of secrets in logs and secure, encrypted session management (AES-256-GCM).
18
+
19
+ ---
20
+
21
+ ## ✨ Key Features
22
+
23
+ - 🛡️ **XSS Protection**: Robust HTML sanitization powered by `sanitize-html` and DOM clobbering prevention.
24
+ - ⚡ **Prototype Pollution Defense**: Secure object merging and safe JSON parsing.
25
+ - 📂 **Path Traversal Mitigation**: Root-locked file system operations.
26
+ - 🔐 **Advanced Auth**: WebAuthn/Passkey verification, JWT signature validation, ZKP challenges, and CSRF protection.
27
+ - 🧬 **Cryptographic Hardening**: AES-256-GCM sessions, strict scrypt hashing.
28
+ - 🚀 **Performance**: CPU-intensive crypto offloaded to Worker Threads.
29
+ - 🚦 **Adaptive Rate Limiting**: Security-aware throttling with Redis cluster support and global kill-switch.
30
+
31
+ ---
32
+
33
+ ## 🚀 Installation
34
+
35
+ ```bash
36
+ npm install mitigator
37
+ ```
38
+
39
+ > [!WARNING]
40
+ > **Browser / Edge runtime — `crypto` namespace collision.**
41
+ > The `crypto` named export shadows the browser's built-in `globalThis.crypto` (Web Crypto API)
42
+ > within the importing module's scope. Use a named alias in browser or edge runtimes:
43
+ >
44
+ > ```typescript
45
+ > import { crypto as mitigatorCrypto } from 'mitigator';
46
+ > ```
47
+
48
+ ---
49
+
50
+ ## 📦 Modules Overview
51
+
52
+ | Module | Description |
53
+ | :---------- | :------------------------------------------------------------------------- |
54
+ | `sanitize` | HTML escaping, tag stripping, and robust sanitization (XSS defense). |
55
+ | `validate` | Schema enforcement, secret scanning, and pwned password checks. |
56
+ | `headers` | Security headers (CSP, HSTS, etc.) and strict CSP builders. |
57
+ | `auth` | WebAuthn, HMAC challenge-response, CSRF, JWT validation, and RBAC helpers. |
58
+ | `crypto` | AES session encryption, SSS, and PQC. |
59
+ | `fs` | Secure path resolution and magic number file type verification. |
60
+ | `http` | URL normalization, TLS fingerprinting, and SRI generation. |
61
+ | `rateLimit` | Adaptive rate limiting and Token Bucket implementations. |
62
+ | `safeJson` | DoS-resistant and prototype-pollution safe JSON parsing. |
63
+ | `safeMerge` | Deep merging protected against prototype pollution. |
64
+ | `utils` | Sensitive data redaction, secure error handling, and prototype lockdown. |
65
+
66
+ ---
67
+
68
+ ## 🛠️ Detailed Usage
69
+
70
+ ### 1. Input Sanitization (`sanitize`)
71
+
72
+ Prevent XSS by cleaning untrusted HTML or escaping characters. Backed by `sanitize-html`.
73
+
74
+ ```typescript
75
+ import { sanitize } from 'mitigator';
76
+
77
+ // Basic HTML escaping
78
+ const escaped = sanitize.escapeHtml('<script>alert("xss")</script>');
79
+
80
+ // Robust HTML sanitization with allowlist
81
+ const clean = sanitize.sanitizeHtml('<p>Hello <script>bad()</script> <b>World</b></p>');
82
+ // Output: <p>Hello <b>World</b></p>
83
+
84
+ // Prevent DOM Clobbering by namespacing IDs/Names
85
+ const safeHtml = sanitize.preventDOMClobbering('<img id="config">');
86
+ // Output: <img id="sk-config">
87
+ ```
88
+
89
+ ### 2. Secure File Operations (`fs`)
90
+
91
+ Stop path traversal attacks by locking file operations to a root directory.
92
+
93
+ ```typescript
94
+ import { fs } from 'mitigator';
95
+
96
+ const root = './uploads';
97
+
98
+ // This will throw if the path attempts to escape './uploads' (e.g., '../../etc/passwd')
99
+ const safePath = fs.resolveSafePath(root, 'user-data.json');
100
+
101
+ // Verify file type by Magic Numbers (more secure than extension check)
102
+ const isPNG = await fs.verifyMagicNumber('image.bin', fs.MAGIC_NUMBERS.PNG);
103
+ ```
104
+
105
+ ### 3. Adaptive Rate Limiting (`rateLimit`)
106
+
107
+ Automatically penalize high-risk actors based on security events.
108
+
109
+ ```typescript
110
+ import { rateLimit } from 'mitigator';
111
+
112
+ const limiter = new rateLimit.AdaptiveRateLimiter({
113
+ standardLimit: 100,
114
+ penaltyLimit: 10, // Strict limit for suspicious users
115
+ windowMs: 60000, // 1 minute
116
+ securityThreshold: 5, // Max security events before penalty
117
+ burstThreshold: 3, // Max bursts before penalty
118
+ });
119
+
120
+ if (await limiter.isLimited('user-ip')) {
121
+ throw new Error('Too many requests');
122
+ }
123
+
124
+ // Record a suspicious event (e.g., failed login)
125
+ await limiter.recordSecurityEvent('user-ip', 1);
126
+ ```
127
+
128
+ ### 4. Safe Object Handling (`safeJson` & `safeMerge`)
129
+
130
+ Protect your application from Prototype Pollution.
131
+
132
+ ```typescript
133
+ import { safeJson, safeMerge } from 'mitigator';
134
+
135
+ // Parse JSON while stripping __proto__ and constructor keys
136
+ const data = safeJson.parse(untrustedString);
137
+
138
+ // Deep merge objects without risking prototype pollution
139
+ const config = safeMerge.merge(defaultConfig, userConfig);
140
+ ```
141
+
142
+ ### 5. Advanced Cryptography (`crypto` & `auth`)
143
+
144
+ Implement high-level security and quantum-resistant patterns with ease.
145
+
146
+ #### Shamir's Secret Sharing (SSS)
147
+
148
+ Split sensitive keys into $M$ cryptographic shares where any $T$ shares can exactly reconstruct the original secret, but fewer than $T$ yields only garbage. Built over Galois Field $GF(256)$ with AES primitive polynomial arithmetic.
149
+
150
+ ```typescript
151
+ import { crypto } from 'mitigator';
152
+
153
+ // Split secret key into 5 shares with a threshold of 3
154
+ const shares = crypto.splitSecret('master-key-content', 5, 3);
155
+
156
+ // Reconstruct with any 3 shares
157
+ const reconstructed = crypto.reconstructSecret([shares[0], shares[2], shares[4]]);
158
+ console.log(reconstructed.toString('utf8')); // 'master-key-content'
159
+ ```
160
+
161
+ #### Post-Quantum Cryptography (PQC)
162
+
163
+ Phishing-resistant, quantum-resistant one-time signatures powered by the standard **Winternitz One-Time Signatures (WOTS)** nibble-chaining hash framework.
164
+
165
+ > [!CAUTION]
166
+ > **WOTS is a ONE-TIME signature scheme.** Each `privateKey` **must only ever sign a single
167
+ > message**. Signing a second message with the same private key leaks enough key material
168
+ > to forge arbitrary signatures, completely breaking the security of the scheme.
169
+ >
170
+ > **Always generate a fresh key pair with `generatePQCKeyPair()` for every message you sign.**
171
+ > Mitigator enforces this at runtime — `signPQC()` throws `WOTS_KEY_REUSE` if you attempt
172
+ > to reuse a private key within the same process.
173
+
174
+ ```typescript
175
+ import { crypto as mitigatorCrypto } from 'mitigator';
176
+
177
+ // Generate key pair
178
+ const { publicKey, privateKey } = mitigatorCrypto.generatePQCKeyPair();
179
+
180
+ // Sign and verify message — each key pair can only sign ONCE
181
+ const signature = mitigatorCrypto.signPQC('quantum-secure-payload', privateKey);
182
+ const isValid = mitigatorCrypto.verifyPQCSignature('quantum-secure-payload', signature, publicKey); // true
183
+
184
+ // ❌ This will throw WOTS_KEY_REUSE:
185
+ // mitigatorCrypto.signPQC('second message', privateKey);
186
+ ```
187
+
188
+ #### HMAC Challenge-Response
189
+
190
+ Server-side mutual authentication using an HMAC challenge-response flow.
191
+
192
+ ```typescript
193
+ import { auth } from 'mitigator';
194
+
195
+ const salt = 'per-user-random-salt';
196
+ const challenge = auth.generateHmacChallenge(salt);
197
+
198
+ // Client computes: HMAC-SHA256(secret, challenge) and sends it back
199
+ const proof = computeClientProof(challenge, sharedSecret);
200
+ const verified = auth.verifyHmacResponse(challenge, proof, sharedSecret); // true
201
+ ```
202
+
203
+ #### FIDO2 WebAuthn / Passkeys
204
+
205
+ Phishing-resistant browser authentication helpers — both registration and assertion flows.
206
+
207
+ ```typescript
208
+ import { auth } from 'mitigator';
209
+
210
+ // Generate base64url registration challenge
211
+ const challenge = auth.generatePasskeyChallenge();
212
+
213
+ // Verify client registration response against expected challenge and origin
214
+ const regResult = auth.verifyPasskeyRegistration(clientDataJSON, challenge, 'https://example.com');
215
+
216
+ // Parse binary authenticatorData buffer to extract credentialIds and keys
217
+ const credentials = auth.parseAuthenticatorData(authDataBuffer);
218
+
219
+ // Verify an authentication assertion (navigator.credentials.get flow)
220
+ // This implements WebAuthn Level 2 §7.2 including replay attack protection.
221
+ const assertResult = auth.verifyPasskeyAssertion(
222
+ clientDataJSON, // from browser response.clientDataJSON
223
+ storedChallenge, // challenge you sent to the browser
224
+ 'https://example.com', // expected origin
225
+ storedSignCount, // signCount from DB (pass 0 on first use)
226
+ authDataBuffer, // from browser response.authenticatorData
227
+ signatureBuffer, // from browser response.signature
228
+ credentialPublicKeyPem, // public key stored during registration
229
+ );
230
+ if (!assertResult.verified) throw new Error(assertResult.error);
231
+ await db.updateSignCount(credentialId, assertResult.newSignCount!);
232
+ ```
233
+
234
+ ---
235
+
236
+ ## 🏗️ Framework Integration
237
+
238
+ Mitigator comes with built-in presets for Express, Fastify, NestJS, and Next.js.
239
+
240
+ ### Express
241
+
242
+ ```typescript
243
+ import express from 'express';
244
+ import { presets } from 'mitigator';
245
+
246
+ const app = express();
247
+
248
+ // Global security middleware (Headers, Rate Limiting, Secret Scanning)
249
+ app.use(presets.expressMiddleware({ rateLimit: true, rateLimitMax: 100 }));
250
+
251
+ // Secure Logger Chain (Tamper-proof logs via cryptographic linking)
252
+ const logger = presets.createSecureLogger(console);
253
+ logger.info('User logged in', { userId: 123 });
254
+
255
+ // Global Secure Error Handler
256
+ app.use(presets.expressErrorHandler);
257
+ ```
258
+
259
+ ### Fastify
260
+
261
+ ```typescript
262
+ import Fastify from 'fastify';
263
+ import { presets } from 'mitigator';
264
+
265
+ const fastify = Fastify();
266
+
267
+ // Register the security hook/plugin preset
268
+ fastify.register(presets.fastifyPlugin({ rateLimit: true, rateLimitMax: 100 }));
269
+ ```
270
+
271
+ ### NestJS
272
+
273
+ ```typescript
274
+ import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
275
+ import { presets } from 'mitigator';
276
+
277
+ @Module({})
278
+ export class AppModule implements NestModule {
279
+ configure(consumer: MiddlewareConsumer) {
280
+ // Configure rate limits
281
+ presets.NestJsMitigatorMiddleware.configure({ rateLimit: true, rateLimitMax: 100 });
282
+
283
+ // Apply globally
284
+ consumer.apply(presets.NestJsMitigatorMiddleware).forRoutes('*');
285
+ }
286
+ }
287
+ ```
288
+
289
+ ### Next.js Edge Middleware
290
+
291
+ ```typescript
292
+ // middleware.ts
293
+ import { NextResponse } from 'next/server';
294
+ import { presets } from 'mitigator';
295
+
296
+ export function middleware(request) {
297
+ const response = NextResponse.next();
298
+ return presets.nextJsMiddleware(request, response);
299
+ }
300
+ ```
301
+
302
+ ---
303
+
304
+ ## 🛡️ Automated Security Audit CLI
305
+
306
+ Mitigator features a recursive command-line security scanner (`mitigator-audit`) to check your configurations and files automatically in pre-commit hooks or CI/CD pipelines.
307
+
308
+ ### Usage
309
+
310
+ ```bash
311
+ # Scan the current directory
312
+ npx mitigator-audit
313
+
314
+ # Scan a specific directory
315
+ npx mitigator-audit ./src
316
+ ```
317
+
318
+ ### Checks Performed
319
+
320
+ - **Hardcoded Secret Detection**: Scans for high-entropy tokens and plain-text API credentials.
321
+ - **Potential Path Traversal**: Scans for raw user inputs mapped directly to file system operations.
322
+ - **Prototype Pollution Risks**: Scans for raw `JSON.parse` or `Object.assign` calls without Prototype Pollution filters.
323
+ - **Header Drift Detection**: Scans for express instances without security headers presets.
324
+
325
+ ---
326
+
327
+ ## 🧪 Safety & Best Practices
328
+
329
+ ### Prototype Lockdown
330
+
331
+ Prevent many prototype pollution attacks globally by freezing core prototypes. **Warning**: This may break some legacy libraries that modify built-ins.
332
+
333
+ ```typescript
334
+ import { utils } from 'mitigator';
335
+
336
+ utils.lockdownPrototypes(); // Freezes Object.prototype, Array.prototype, etc.
337
+ ```
338
+
339
+ ### Memory Wiping
340
+
341
+ For extremely sensitive data (like decrypted keys), overwrite the buffer once finished.
342
+
343
+ ```typescript
344
+ import { utils } from 'mitigator';
345
+
346
+ const keyBuffer = Buffer.from('high-entropy-secret');
347
+ // ... use key ...
348
+ utils.wipeBuffer(keyBuffer); // Fills buffer with zeros
349
+ ```
350
+
351
+ ---
352
+
353
+ ## 📜 License
354
+
355
+ ISC License - see [LICENSE](LICENSE) for details.
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/bin/mitigator-audit.ts
32
+ var mitigator_audit_exports = {};
33
+ __export(mitigator_audit_exports, {
34
+ main: () => main,
35
+ runAudit: () => runAudit
36
+ });
37
+ module.exports = __toCommonJS(mitigator_audit_exports);
38
+ var import_node_fs = require("fs");
39
+ var import_node_path = require("path");
40
+
41
+ // src/validate/index.ts
42
+ var import_node_crypto = require("crypto");
43
+ var https = __toESM(require("https"), 1);
44
+ var SECRET_PATTERNS = [
45
+ /AKIA[0-9A-Z]{16}/,
46
+ /-----BEGIN (RSA|OPENSSH|EC|PGP) PRIVATE KEY-----/,
47
+ /ghp_[a-zA-Z0-9]{36}/,
48
+ /sk_live_[a-zA-Z0-9]{24}/
49
+ ];
50
+ var scanForSecrets = (input) => {
51
+ if (typeof input === "string") {
52
+ return SECRET_PATTERNS.some((pattern) => pattern.test(input));
53
+ }
54
+ if (typeof input === "object" && input !== null) {
55
+ return Object.values(input).some(scanForSecrets);
56
+ }
57
+ return false;
58
+ };
59
+
60
+ // src/bin/mitigator-audit.ts
61
+ var findFiles = (dir, fileList = []) => {
62
+ try {
63
+ const files = (0, import_node_fs.readdirSync)(dir);
64
+ for (const file of files) {
65
+ if (file === "node_modules" || file === ".git" || file === "dist" || file === "coverage")
66
+ continue;
67
+ const filePath = (0, import_node_path.join)(dir, file);
68
+ const stat = (0, import_node_fs.statSync)(filePath);
69
+ if (stat.isDirectory()) {
70
+ findFiles(filePath, fileList);
71
+ } else {
72
+ const ext = (0, import_node_path.extname)(file);
73
+ if ([".js", ".ts", ".json", ".env", ".yml", ".yaml"].includes(ext)) {
74
+ fileList.push(filePath);
75
+ }
76
+ }
77
+ }
78
+ } catch {
79
+ }
80
+ return fileList;
81
+ };
82
+ var auditFile = (filePath) => {
83
+ const result = { filePath, vulnerabilities: [] };
84
+ try {
85
+ const content = (0, import_node_fs.readFileSync)(filePath, "utf8");
86
+ const ext = (0, import_node_path.extname)(filePath);
87
+ if (ext !== ".json") {
88
+ const lines = content.split(/\r?\n/);
89
+ lines.forEach((line, idx) => {
90
+ if (scanForSecrets(line)) {
91
+ result.vulnerabilities.push({
92
+ type: "Hardcoded Secret",
93
+ severity: "HIGH",
94
+ message: `Potential plaintext API key or credential leak detected.`,
95
+ line: idx + 1
96
+ });
97
+ }
98
+ });
99
+ }
100
+ if (ext === ".js" || ext === ".ts") {
101
+ const traversalRegex = /fs\.(readFileSync|writeFileSync|readFile|writeFile)\(.*req\.(query|body|params)\./;
102
+ if (traversalRegex.test(content)) {
103
+ result.vulnerabilities.push({
104
+ type: "Potential Path Traversal",
105
+ severity: "HIGH",
106
+ message: "Direct user input passed to a file system operation without path lock validation."
107
+ });
108
+ }
109
+ if (content.includes("Object.assign(") || content.includes("JSON.parse(")) {
110
+ if (!content.includes("safeMerge") && !content.includes("safeJson") && !content.includes("lockdownPrototypes")) {
111
+ result.vulnerabilities.push({
112
+ type: "Prototype Pollution Risk",
113
+ severity: "MEDIUM",
114
+ message: "Raw JSON parsing or object assignment used without Prototype Pollution defense."
115
+ });
116
+ }
117
+ }
118
+ if (content.includes("express()") && !content.includes("presets.expressMiddleware") && !content.includes("helmet")) {
119
+ result.vulnerabilities.push({
120
+ type: "Missing Security Headers",
121
+ severity: "MEDIUM",
122
+ message: "Express application instance created but no security middleware preset detected."
123
+ });
124
+ }
125
+ }
126
+ } catch {
127
+ }
128
+ return result;
129
+ };
130
+ var runAudit = (targetDir = ".") => {
131
+ const files = findFiles(targetDir);
132
+ const results = [];
133
+ for (const file of files) {
134
+ const res = auditFile(file);
135
+ if (res.vulnerabilities.length > 0) {
136
+ results.push(res);
137
+ }
138
+ }
139
+ return results;
140
+ };
141
+ var main = () => {
142
+ const args = process.argv.slice(2);
143
+ const target = args[0] || ".";
144
+ console.log(
145
+ `\u{1F6E1}\uFE0F Mitigator Security Audit: Scanning [${target}] for vulnerabilities and configuration drifts...
146
+ `
147
+ );
148
+ const results = runAudit(target);
149
+ let totalHigh = 0;
150
+ let totalMedium = 0;
151
+ if (results.length === 0) {
152
+ console.log("\u2705 No security vulnerabilities or drifts found. Keep up the high standard!");
153
+ process.exit(0);
154
+ }
155
+ results.forEach((res) => {
156
+ console.log(`\u{1F4C2} File: ${res.filePath}`);
157
+ res.vulnerabilities.forEach((vuln) => {
158
+ const color = vuln.severity === "HIGH" ? "\x1B[31m[HIGH]\x1B[0m" : "\x1B[33m[MEDIUM]\x1B[0m";
159
+ if (vuln.severity === "HIGH") totalHigh++;
160
+ if (vuln.severity === "MEDIUM") totalMedium++;
161
+ const lineStr = vuln.line ? ` (line ${vuln.line})` : "";
162
+ console.log(` ${color} ${vuln.type}: ${vuln.message}${lineStr}`);
163
+ });
164
+ console.log("");
165
+ });
166
+ console.log(
167
+ `\u{1F4CA} Audit Summary: Found ${totalHigh} HIGH and ${totalMedium} MEDIUM severity alerts.`
168
+ );
169
+ if (totalHigh > 0) {
170
+ console.log(
171
+ "\x1B[31m\u274C Audit Failed: Critical vulnerabilities must be resolved before merging.\x1B[0m"
172
+ );
173
+ process.exit(1);
174
+ } else {
175
+ console.log("\x1B[32m\u26A0\uFE0F Audit Passed with warnings.\x1B[0m");
176
+ process.exit(0);
177
+ }
178
+ };
179
+ if (typeof require !== "undefined" && require.main === module || process.argv[1] && (process.argv[1].endsWith("mitigator-audit") || process.argv[1].endsWith("mitigator-audit.js") || process.argv[1].endsWith("mitigator-audit.ts"))) {
180
+ main();
181
+ }
182
+ // Annotate the CommonJS export names for ESM import in node:
183
+ 0 && (module.exports = {
184
+ main,
185
+ runAudit
186
+ });
187
+ //# sourceMappingURL=mitigator-audit.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/bin/mitigator-audit.ts","../../src/validate/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFileSync, readdirSync, statSync } from 'node:fs';\nimport { join, extname } from 'node:path';\nimport { scanForSecrets } from '../validate/index.js';\n\ninterface AuditResult {\n filePath: string;\n vulnerabilities: {\n type: string;\n severity: 'HIGH' | 'MEDIUM' | 'LOW';\n message: string;\n line?: number;\n }[];\n}\n\nconst findFiles = (dir: string, fileList: string[] = []): string[] => {\n try {\n const files = readdirSync(dir);\n for (const file of files) {\n if (file === 'node_modules' || file === '.git' || file === 'dist' || file === 'coverage')\n continue;\n const filePath = join(dir, file);\n const stat = statSync(filePath);\n if (stat.isDirectory()) {\n findFiles(filePath, fileList);\n } else {\n const ext = extname(file);\n if (['.js', '.ts', '.json', '.env', '.yml', '.yaml'].includes(ext)) {\n fileList.push(filePath);\n }\n }\n }\n } catch {}\n return fileList;\n};\n\nconst auditFile = (filePath: string): AuditResult => {\n const result: AuditResult = { filePath, vulnerabilities: [] };\n try {\n const content = readFileSync(filePath, 'utf8');\n const ext = extname(filePath);\n\n // 1. Plaintext Secret Scanning\n if (ext !== '.json') {\n const lines = content.split(/\\r?\\n/);\n lines.forEach((line, idx) => {\n if (scanForSecrets(line)) {\n result.vulnerabilities.push({\n type: 'Hardcoded Secret',\n severity: 'HIGH',\n message: `Potential plaintext API key or credential leak detected.`,\n line: idx + 1,\n });\n }\n });\n }\n\n // 2. Dangerous File Sync / Path Traversal\n if (ext === '.js' || ext === '.ts') {\n const traversalRegex =\n /fs\\.(readFileSync|writeFileSync|readFile|writeFile)\\(.*req\\.(query|body|params)\\./;\n if (traversalRegex.test(content)) {\n result.vulnerabilities.push({\n type: 'Potential Path Traversal',\n severity: 'HIGH',\n message:\n 'Direct user input passed to a file system operation without path lock validation.',\n });\n }\n\n // 3. Unsafe Merging without prototype check\n if (content.includes('Object.assign(') || content.includes('JSON.parse(')) {\n if (\n !content.includes('safeMerge') &&\n !content.includes('safeJson') &&\n !content.includes('lockdownPrototypes')\n ) {\n result.vulnerabilities.push({\n type: 'Prototype Pollution Risk',\n severity: 'MEDIUM',\n message:\n 'Raw JSON parsing or object assignment used without Prototype Pollution defense.',\n });\n }\n }\n\n // 4. Missing secure headers in standard http/express setups\n if (\n content.includes('express()') &&\n !content.includes('presets.expressMiddleware') &&\n !content.includes('helmet')\n ) {\n result.vulnerabilities.push({\n type: 'Missing Security Headers',\n severity: 'MEDIUM',\n message:\n 'Express application instance created but no security middleware preset detected.',\n });\n }\n }\n } catch {}\n return result;\n};\n\nexport const runAudit = (targetDir: string = '.'): AuditResult[] => {\n const files = findFiles(targetDir);\n const results: AuditResult[] = [];\n for (const file of files) {\n const res = auditFile(file);\n if (res.vulnerabilities.length > 0) {\n results.push(res);\n }\n }\n return results;\n};\n\n// Main Execution\nexport const main = () => {\n const args = process.argv.slice(2);\n const target = args[0] || '.';\n console.log(\n `🛡️ Mitigator Security Audit: Scanning [${target}] for vulnerabilities and configuration drifts...\\n`,\n );\n\n const results = runAudit(target);\n let totalHigh = 0;\n let totalMedium = 0;\n\n if (results.length === 0) {\n console.log('✅ No security vulnerabilities or drifts found. Keep up the high standard!');\n process.exit(0);\n }\n\n results.forEach((res) => {\n console.log(`📂 File: ${res.filePath}`);\n res.vulnerabilities.forEach((vuln) => {\n const color = vuln.severity === 'HIGH' ? '\\x1b[31m[HIGH]\\x1b[0m' : '\\x1b[33m[MEDIUM]\\x1b[0m';\n if (vuln.severity === 'HIGH') totalHigh++;\n if (vuln.severity === 'MEDIUM') totalMedium++;\n const lineStr = vuln.line ? ` (line ${vuln.line})` : '';\n console.log(` ${color} ${vuln.type}: ${vuln.message}${lineStr}`);\n });\n console.log('');\n });\n\n console.log(\n `📊 Audit Summary: Found ${totalHigh} HIGH and ${totalMedium} MEDIUM severity alerts.`,\n );\n if (totalHigh > 0) {\n console.log(\n '\\x1b[31m❌ Audit Failed: Critical vulnerabilities must be resolved before merging.\\x1b[0m',\n );\n process.exit(1);\n } else {\n console.log('\\x1b[32m⚠️ Audit Passed with warnings.\\x1b[0m');\n process.exit(0);\n }\n};\n\n// Only execute when run directly\n/* v8 ignore next 11 */\nif (\n (typeof require !== 'undefined' && require.main === module) ||\n (process.argv[1] &&\n (process.argv[1].endsWith('mitigator-audit') ||\n process.argv[1].endsWith('mitigator-audit.js') ||\n process.argv[1].endsWith('mitigator-audit.ts')))\n) {\n main();\n}\n","import { createHash } from 'node:crypto';\nimport * as https from 'node:https';\n\n/**\n * Result returned by `checkPwnedPassword`.\n * Always resolves (never rejects) to preserve fail-open availability semantics.\n */\nexport interface CheckPwnedResult {\n /** Number of times this password appeared in known data breaches. 0 if not found. */\n count: number;\n /**\n * Whether the HIBP API was reachable during this check.\n * If `false`, the result is inconclusive — the password may or may not be compromised.\n * Callers should treat `apiAvailable: false` as a signal to retry or log a warning.\n */\n apiAvailable: boolean;\n}\n\n/**\n * Checks if a password has been leaked in a data breach using the Have I Been Pwned (HIBP) API.\n * Uses k-Anonymity (sending only the first 5 characters of the SHA-1 hash) to ensure\n * the password is never exposed to the API.\n *\n * Always resolves — never rejects. If the API is unreachable, `apiAvailable` will be `false`\n * and `count` will be `0` (inconclusive). Callers should check `apiAvailable` before\n * treating a zero count as \"password is clean\".\n *\n * @param password The password to check.\n * @returns {Promise<CheckPwnedResult>} Structured result with breach count and API availability.\n *\n * @example\n * const { count, apiAvailable } = await checkPwnedPassword('hunter2');\n * if (!apiAvailable) logger.warn('HIBP API unreachable — skipping pwned check');\n * else if (count > 0) throw new Error('Password found in data breaches');\n */\nexport const checkPwnedPassword = (password: string): Promise<CheckPwnedResult> => {\n return new Promise((resolve) => {\n const hash = createHash('sha1').update(password).digest('hex').toUpperCase();\n const prefix = hash.slice(0, 5);\n const suffix = hash.slice(5);\n\n https\n .get(`https://api.pwnedpasswords.com/range/${prefix}`, (res) => {\n let data = '';\n res.on('data', (chunk) => (data += chunk));\n res.on('end', () => {\n const lines = data.split('\\n');\n for (const line of lines) {\n const [hashSuffix, count] = line.split(':');\n if (hashSuffix === suffix) {\n return resolve({ count: Number.parseInt(count.trim()), apiAvailable: true });\n }\n }\n resolve({ count: 0, apiAvailable: true });\n });\n })\n .on('error', (err) => {\n // Fail-open: don't block authentication when HIBP is unreachable.\n // apiAvailable: false lets the caller decide how to handle the degraded state.\n console.error('Mitigator: HIBP API connection error.', err);\n resolve({ count: 0, apiAvailable: false });\n });\n });\n};\n\n/**\n * Patterns for secrets.\n */\nexport const SECRET_PATTERNS = [\n /AKIA[0-9A-Z]{16}/,\n /-----BEGIN (RSA|OPENSSH|EC|PGP) PRIVATE KEY-----/,\n /ghp_[a-zA-Z0-9]{36}/,\n /sk_live_[a-zA-Z0-9]{24}/,\n];\n\n/**\n * Scans for secrets.\n */\nexport const scanForSecrets = (input: any): boolean => {\n if (typeof input === 'string') {\n return SECRET_PATTERNS.some((pattern) => pattern.test(input));\n }\n if (typeof input === 'object' && input !== null) {\n return Object.values(input).some(scanForSecrets);\n }\n return false;\n};\n\n/**\n * Weak password check.\n */\nexport const isWeakPassword = (password: string): boolean => {\n if (password.length < 8) return true;\n const hasLower = /[a-z]/.test(password);\n const hasUpper = /[A-Z]/.test(password);\n const hasNumber = /\\d/.test(password);\n const hasSpecial = /[^a-zA-Z0-9]/.test(password);\n const types = [hasLower, hasUpper, hasNumber, hasSpecial].filter(Boolean).length;\n if (types < 2) return true;\n const common = ['password', '123456', 'qwerty', 'admin123', 'password123', '12345678'];\n if (common.includes(password.toLowerCase())) return true;\n return false;\n};\n\n/**\n * Schema types.\n */\nexport type Schema = {\n [key: string]: 'string' | 'number' | 'boolean' | 'object' | 'array';\n};\n\n/**\n * Type validation.\n */\nexport const isType = (val: any, type: Schema[keyof Schema]): boolean => {\n if (type === 'array') return Array.isArray(val);\n return typeof val === type && val !== null;\n};\n\n/**\n * Schema enforcement.\n */\nexport const enforceSchema = <T extends Record<string, any>>(\n data: any,\n schema: Schema,\n): T | null => {\n if (typeof data !== 'object' || data === null || Array.isArray(data)) return null;\n const result: any = {};\n for (const key of Object.keys(schema)) {\n const expectedType = schema[key];\n const value = data[key];\n if (value === undefined || !isType(value, expectedType)) return null;\n result[key] = value;\n }\n return result;\n};\n\n/**\n * Email validation.\n */\nexport const isEmail = (input: string): boolean => {\n 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(\n input,\n );\n};\n\n/**\n * Injection pattern detection (Heuristic).\n * Detects common SQL, NoSQL, and Command Injection payloads.\n */\nexport const hasInjectionPattern = (input: string): boolean => {\n if (typeof input !== 'string') return false;\n\n const dangerousPatterns = [\n // SQL Injection\n /(\\b(SELECT|INSERT|UPDATE|DELETE|DROP|ALTER|CREATE|EXEC|UNION|ALL|ANY|SOME)\\b.*\\b(FROM|INTO|SET|TABLE|DATABASE)\\b)/i,\n /'\\s*OR\\s+'?1'?\\s*=\\s*'?1/i,\n /\"\\s*OR\\s+\"?1\"?\\s*=\\s*\"?1/i,\n /--\\s*$/,\n /;\\s*(WAITFOR|DELAY|SLEEP)/i,\n /;\\s*(EXEC|EXECUTE)\\b/i,\n // NoSQL Injection\n /\\$(where|gt|lt|gte|lte|ne|in|nin|regex|expr|eq)/i,\n /\\{\\s*\\$ne\\s*:/i,\n // Command Injection\n /(;|\\||&&|\\|\\||`|\\$)\\s*(cat|ls|pwd|whoami|id|echo|bash|sh|ping|curl|wget)/i,\n ];\n return dangerousPatterns.some((pattern) => pattern.test(input));\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ interface AuditResult {
3
+ filePath: string;
4
+ vulnerabilities: {
5
+ type: string;
6
+ severity: 'HIGH' | 'MEDIUM' | 'LOW';
7
+ message: string;
8
+ line?: number;
9
+ }[];
10
+ }
11
+ declare const runAudit: (targetDir?: string) => AuditResult[];
12
+ declare const main: () => never;
13
+
14
+ export { main, runAudit };
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ interface AuditResult {
3
+ filePath: string;
4
+ vulnerabilities: {
5
+ type: string;
6
+ severity: 'HIGH' | 'MEDIUM' | 'LOW';
7
+ message: string;
8
+ line?: number;
9
+ }[];
10
+ }
11
+ declare const runAudit: (targetDir?: string) => AuditResult[];
12
+ declare const main: () => never;
13
+
14
+ export { main, runAudit };