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 +15 -0
- package/README.md +355 -0
- package/dist/bin/mitigator-audit.cjs +187 -0
- package/dist/bin/mitigator-audit.cjs.map +1 -0
- package/dist/bin/mitigator-audit.d.cts +14 -0
- package/dist/bin/mitigator-audit.d.ts +14 -0
- package/dist/bin/mitigator-audit.js +135 -0
- package/dist/bin/mitigator-audit.js.map +1 -0
- package/dist/chunk-TT2DUALY.js +123 -0
- package/dist/chunk-TT2DUALY.js.map +1 -0
- package/dist/index.cjs +1643 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1116 -0
- package/dist/index.d.ts +1116 -0
- package/dist/index.js +1501 -0
- package/dist/index.js.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
__require,
|
|
4
|
+
scanForSecrets
|
|
5
|
+
} from "../chunk-TT2DUALY.js";
|
|
6
|
+
|
|
7
|
+
// src/bin/mitigator-audit.ts
|
|
8
|
+
import { readFileSync, readdirSync, statSync } from "fs";
|
|
9
|
+
import { join, extname } from "path";
|
|
10
|
+
var findFiles = (dir, fileList = []) => {
|
|
11
|
+
try {
|
|
12
|
+
const files = readdirSync(dir);
|
|
13
|
+
for (const file of files) {
|
|
14
|
+
if (file === "node_modules" || file === ".git" || file === "dist" || file === "coverage")
|
|
15
|
+
continue;
|
|
16
|
+
const filePath = join(dir, file);
|
|
17
|
+
const stat = statSync(filePath);
|
|
18
|
+
if (stat.isDirectory()) {
|
|
19
|
+
findFiles(filePath, fileList);
|
|
20
|
+
} else {
|
|
21
|
+
const ext = extname(file);
|
|
22
|
+
if ([".js", ".ts", ".json", ".env", ".yml", ".yaml"].includes(ext)) {
|
|
23
|
+
fileList.push(filePath);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
return fileList;
|
|
30
|
+
};
|
|
31
|
+
var auditFile = (filePath) => {
|
|
32
|
+
const result = { filePath, vulnerabilities: [] };
|
|
33
|
+
try {
|
|
34
|
+
const content = readFileSync(filePath, "utf8");
|
|
35
|
+
const ext = extname(filePath);
|
|
36
|
+
if (ext !== ".json") {
|
|
37
|
+
const lines = content.split(/\r?\n/);
|
|
38
|
+
lines.forEach((line, idx) => {
|
|
39
|
+
if (scanForSecrets(line)) {
|
|
40
|
+
result.vulnerabilities.push({
|
|
41
|
+
type: "Hardcoded Secret",
|
|
42
|
+
severity: "HIGH",
|
|
43
|
+
message: `Potential plaintext API key or credential leak detected.`,
|
|
44
|
+
line: idx + 1
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (ext === ".js" || ext === ".ts") {
|
|
50
|
+
const traversalRegex = /fs\.(readFileSync|writeFileSync|readFile|writeFile)\(.*req\.(query|body|params)\./;
|
|
51
|
+
if (traversalRegex.test(content)) {
|
|
52
|
+
result.vulnerabilities.push({
|
|
53
|
+
type: "Potential Path Traversal",
|
|
54
|
+
severity: "HIGH",
|
|
55
|
+
message: "Direct user input passed to a file system operation without path lock validation."
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
if (content.includes("Object.assign(") || content.includes("JSON.parse(")) {
|
|
59
|
+
if (!content.includes("safeMerge") && !content.includes("safeJson") && !content.includes("lockdownPrototypes")) {
|
|
60
|
+
result.vulnerabilities.push({
|
|
61
|
+
type: "Prototype Pollution Risk",
|
|
62
|
+
severity: "MEDIUM",
|
|
63
|
+
message: "Raw JSON parsing or object assignment used without Prototype Pollution defense."
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (content.includes("express()") && !content.includes("presets.expressMiddleware") && !content.includes("helmet")) {
|
|
68
|
+
result.vulnerabilities.push({
|
|
69
|
+
type: "Missing Security Headers",
|
|
70
|
+
severity: "MEDIUM",
|
|
71
|
+
message: "Express application instance created but no security middleware preset detected."
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
};
|
|
79
|
+
var runAudit = (targetDir = ".") => {
|
|
80
|
+
const files = findFiles(targetDir);
|
|
81
|
+
const results = [];
|
|
82
|
+
for (const file of files) {
|
|
83
|
+
const res = auditFile(file);
|
|
84
|
+
if (res.vulnerabilities.length > 0) {
|
|
85
|
+
results.push(res);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return results;
|
|
89
|
+
};
|
|
90
|
+
var main = () => {
|
|
91
|
+
const args = process.argv.slice(2);
|
|
92
|
+
const target = args[0] || ".";
|
|
93
|
+
console.log(
|
|
94
|
+
`\u{1F6E1}\uFE0F Mitigator Security Audit: Scanning [${target}] for vulnerabilities and configuration drifts...
|
|
95
|
+
`
|
|
96
|
+
);
|
|
97
|
+
const results = runAudit(target);
|
|
98
|
+
let totalHigh = 0;
|
|
99
|
+
let totalMedium = 0;
|
|
100
|
+
if (results.length === 0) {
|
|
101
|
+
console.log("\u2705 No security vulnerabilities or drifts found. Keep up the high standard!");
|
|
102
|
+
process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
results.forEach((res) => {
|
|
105
|
+
console.log(`\u{1F4C2} File: ${res.filePath}`);
|
|
106
|
+
res.vulnerabilities.forEach((vuln) => {
|
|
107
|
+
const color = vuln.severity === "HIGH" ? "\x1B[31m[HIGH]\x1B[0m" : "\x1B[33m[MEDIUM]\x1B[0m";
|
|
108
|
+
if (vuln.severity === "HIGH") totalHigh++;
|
|
109
|
+
if (vuln.severity === "MEDIUM") totalMedium++;
|
|
110
|
+
const lineStr = vuln.line ? ` (line ${vuln.line})` : "";
|
|
111
|
+
console.log(` ${color} ${vuln.type}: ${vuln.message}${lineStr}`);
|
|
112
|
+
});
|
|
113
|
+
console.log("");
|
|
114
|
+
});
|
|
115
|
+
console.log(
|
|
116
|
+
`\u{1F4CA} Audit Summary: Found ${totalHigh} HIGH and ${totalMedium} MEDIUM severity alerts.`
|
|
117
|
+
);
|
|
118
|
+
if (totalHigh > 0) {
|
|
119
|
+
console.log(
|
|
120
|
+
"\x1B[31m\u274C Audit Failed: Critical vulnerabilities must be resolved before merging.\x1B[0m"
|
|
121
|
+
);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
} else {
|
|
124
|
+
console.log("\x1B[32m\u26A0\uFE0F Audit Passed with warnings.\x1B[0m");
|
|
125
|
+
process.exit(0);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
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"))) {
|
|
129
|
+
main();
|
|
130
|
+
}
|
|
131
|
+
export {
|
|
132
|
+
main,
|
|
133
|
+
runAudit
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=mitigator-audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/bin/mitigator-audit.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"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/validate/index.ts
|
|
14
|
+
var validate_exports = {};
|
|
15
|
+
__export(validate_exports, {
|
|
16
|
+
SECRET_PATTERNS: () => SECRET_PATTERNS,
|
|
17
|
+
checkPwnedPassword: () => checkPwnedPassword,
|
|
18
|
+
enforceSchema: () => enforceSchema,
|
|
19
|
+
hasInjectionPattern: () => hasInjectionPattern,
|
|
20
|
+
isEmail: () => isEmail,
|
|
21
|
+
isType: () => isType,
|
|
22
|
+
isWeakPassword: () => isWeakPassword,
|
|
23
|
+
scanForSecrets: () => scanForSecrets
|
|
24
|
+
});
|
|
25
|
+
import { createHash } from "crypto";
|
|
26
|
+
import * as https from "https";
|
|
27
|
+
var checkPwnedPassword = (password) => {
|
|
28
|
+
return new Promise((resolve) => {
|
|
29
|
+
const hash = createHash("sha1").update(password).digest("hex").toUpperCase();
|
|
30
|
+
const prefix = hash.slice(0, 5);
|
|
31
|
+
const suffix = hash.slice(5);
|
|
32
|
+
https.get(`https://api.pwnedpasswords.com/range/${prefix}`, (res) => {
|
|
33
|
+
let data = "";
|
|
34
|
+
res.on("data", (chunk) => data += chunk);
|
|
35
|
+
res.on("end", () => {
|
|
36
|
+
const lines = data.split("\n");
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
const [hashSuffix, count] = line.split(":");
|
|
39
|
+
if (hashSuffix === suffix) {
|
|
40
|
+
return resolve({ count: Number.parseInt(count.trim()), apiAvailable: true });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
resolve({ count: 0, apiAvailable: true });
|
|
44
|
+
});
|
|
45
|
+
}).on("error", (err) => {
|
|
46
|
+
console.error("Mitigator: HIBP API connection error.", err);
|
|
47
|
+
resolve({ count: 0, apiAvailable: false });
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
var SECRET_PATTERNS = [
|
|
52
|
+
/AKIA[0-9A-Z]{16}/,
|
|
53
|
+
/-----BEGIN (RSA|OPENSSH|EC|PGP) PRIVATE KEY-----/,
|
|
54
|
+
/ghp_[a-zA-Z0-9]{36}/,
|
|
55
|
+
/sk_live_[a-zA-Z0-9]{24}/
|
|
56
|
+
];
|
|
57
|
+
var scanForSecrets = (input) => {
|
|
58
|
+
if (typeof input === "string") {
|
|
59
|
+
return SECRET_PATTERNS.some((pattern) => pattern.test(input));
|
|
60
|
+
}
|
|
61
|
+
if (typeof input === "object" && input !== null) {
|
|
62
|
+
return Object.values(input).some(scanForSecrets);
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
};
|
|
66
|
+
var isWeakPassword = (password) => {
|
|
67
|
+
if (password.length < 8) return true;
|
|
68
|
+
const hasLower = /[a-z]/.test(password);
|
|
69
|
+
const hasUpper = /[A-Z]/.test(password);
|
|
70
|
+
const hasNumber = /\d/.test(password);
|
|
71
|
+
const hasSpecial = /[^a-zA-Z0-9]/.test(password);
|
|
72
|
+
const types = [hasLower, hasUpper, hasNumber, hasSpecial].filter(Boolean).length;
|
|
73
|
+
if (types < 2) return true;
|
|
74
|
+
const common = ["password", "123456", "qwerty", "admin123", "password123", "12345678"];
|
|
75
|
+
if (common.includes(password.toLowerCase())) return true;
|
|
76
|
+
return false;
|
|
77
|
+
};
|
|
78
|
+
var isType = (val, type) => {
|
|
79
|
+
if (type === "array") return Array.isArray(val);
|
|
80
|
+
return typeof val === type && val !== null;
|
|
81
|
+
};
|
|
82
|
+
var enforceSchema = (data, schema) => {
|
|
83
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) return null;
|
|
84
|
+
const result = {};
|
|
85
|
+
for (const key of Object.keys(schema)) {
|
|
86
|
+
const expectedType = schema[key];
|
|
87
|
+
const value = data[key];
|
|
88
|
+
if (value === void 0 || !isType(value, expectedType)) return null;
|
|
89
|
+
result[key] = value;
|
|
90
|
+
}
|
|
91
|
+
return result;
|
|
92
|
+
};
|
|
93
|
+
var isEmail = (input) => {
|
|
94
|
+
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(
|
|
95
|
+
input
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
var hasInjectionPattern = (input) => {
|
|
99
|
+
if (typeof input !== "string") return false;
|
|
100
|
+
const dangerousPatterns = [
|
|
101
|
+
// SQL Injection
|
|
102
|
+
/(\b(SELECT|INSERT|UPDATE|DELETE|DROP|ALTER|CREATE|EXEC|UNION|ALL|ANY|SOME)\b.*\b(FROM|INTO|SET|TABLE|DATABASE)\b)/i,
|
|
103
|
+
/'\s*OR\s+'?1'?\s*=\s*'?1/i,
|
|
104
|
+
/"\s*OR\s+"?1"?\s*=\s*"?1/i,
|
|
105
|
+
/--\s*$/,
|
|
106
|
+
/;\s*(WAITFOR|DELAY|SLEEP)/i,
|
|
107
|
+
/;\s*(EXEC|EXECUTE)\b/i,
|
|
108
|
+
// NoSQL Injection
|
|
109
|
+
/\$(where|gt|lt|gte|lte|ne|in|nin|regex|expr|eq)/i,
|
|
110
|
+
/\{\s*\$ne\s*:/i,
|
|
111
|
+
// Command Injection
|
|
112
|
+
/(;|\||&&|\|\||`|\$)\s*(cat|ls|pwd|whoami|id|echo|bash|sh|ping|curl|wget)/i
|
|
113
|
+
];
|
|
114
|
+
return dangerousPatterns.some((pattern) => pattern.test(input));
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export {
|
|
118
|
+
__require,
|
|
119
|
+
__export,
|
|
120
|
+
scanForSecrets,
|
|
121
|
+
validate_exports
|
|
122
|
+
};
|
|
123
|
+
//# sourceMappingURL=chunk-TT2DUALY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/validate/index.ts"],"sourcesContent":["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":[]}
|