JB/web/scripts/security-scan.js

148 lines
5.1 KiB
JavaScript

const http = require("http");
const BASE_URL = "http://127.0.0.1:3000";
const ROUTES = [
"/",
"/login",
"/register",
"/forgot-password",
"/reset-password",
"/privacy",
"/terms",
"/jobs",
"/companies",
"/api/health",
"/api/jobs",
"/api/companies",
];
const INJECTION_PAYLOADS = [
"' OR '1'='1",
"<script>alert(1)</script>",
"\"><img src=x onerror=alert(1)>",
"../../../../etc/passwd",
"%00",
];
function request(path, options = {}) {
return new Promise((resolve, reject) => {
const url = new URL(path, BASE_URL);
const req = http.request(url, options, (res) => {
let data = "";
res.on("data", (chunk) => (data += chunk));
res.on("end", () => resolve({ status: res.statusCode, headers: res.headers, body: data }));
});
req.on("error", reject);
if (options.body) {
req.write(options.body);
}
req.end();
});
}
async function runSecurityScan() {
console.log("=================================================");
console.log(" AUTOMATED OWASP DYNAMIC SECURITY AUDIT SCAN ");
console.log("=================================================\n");
const findings = [];
let testsCount = 0;
// 1. Security Headers Audit
console.log(">>> [1/4] Auditing Security Headers on Routes...");
for (const route of ROUTES) {
testsCount++;
try {
const res = await request(route);
const h = res.headers;
if (!h["x-frame-options"]) {
findings.push({ severity: "Medium", issue: "Missing X-Frame-Options header", target: route });
}
if (!h["x-content-type-options"]) {
findings.push({ severity: "Low", issue: "Missing X-Content-Type-Options header", target: route });
}
if (!h["content-security-policy"]) {
findings.push({ severity: "Medium", issue: "Missing Content-Security-Policy header", target: route });
}
if (!h["strict-transport-security"]) {
// HSTS is only required over HTTPS, flag as Info for HTTP
findings.push({ severity: "Info", issue: "HSTS header absent over plain HTTP test listener", target: route });
}
} catch (err) {
findings.push({ severity: "High", issue: `Route request error: ${err.message}`, target: route });
}
}
console.log(` Checked ${ROUTES.length} routes for standard OWASP security headers.`);
// 2. Reflected XSS & Injection on Query Parameters
console.log(">>> [2/4] Testing Parameter Injection & Reflected XSS...");
for (const payload of INJECTION_PAYLOADS) {
testsCount++;
const testPath = `/jobs?search=${encodeURIComponent(payload)}&location=${encodeURIComponent(payload)}`;
const res = await request(testPath);
if (res.body.includes(payload) && !res.body.includes("&lt;script&gt;")) {
// If raw unescaped script tag is in html body
if (payload.includes("<script>") && res.body.includes("<script>alert(1)</script>")) {
findings.push({ severity: "High", issue: "Reflected XSS Vulnerability in search param", target: testPath });
}
}
if (res.status === 500) {
findings.push({ severity: "High", issue: "Unhandled server exception on injection payload", target: testPath });
}
}
// 3. API Input Handling & Content-Type Sniffing
console.log(">>> [3/4] Testing API Malformed Body Handling & Sensitive Disclosure...");
testsCount++;
const badJsonRes = await request("/api/auth/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: "{ malformed: json ",
});
if (badJsonRes.status === 500) {
findings.push({ severity: "Medium", issue: "Server 500 on malformed JSON body", target: "/api/auth/register" });
}
// Check if stack traces leak into client response
if (badJsonRes.body.includes("node_modules") || badJsonRes.body.includes("at Object.<anonymous>")) {
findings.push({ severity: "High", issue: "Stack trace / internal path leaked in error response", target: "/api/auth/register" });
}
// 4. Rate Limiting Probe on Auth Endpoints
console.log(">>> [4/4] Verifying Rate Limit Abuse Prevention Protection...");
testsCount++;
let rateLimitHit = false;
for (let i = 0; i < 7; i++) {
const r = await request("/api/auth/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: `test${i}@spam.com`, password: "short" }),
});
if (r.status === 429) {
rateLimitHit = true;
break;
}
}
if (!rateLimitHit) {
findings.push({ severity: "High", issue: "Rate limiter failed to block rapid registration requests", target: "/api/auth/register" });
}
console.log("\n=================================================");
console.log(` SCAN COMPLETE: ${testsCount} tests run`);
console.log(` TOTAL FINDINGS: ${findings.length}`);
console.log("=================================================\n");
console.log("RAW SECURITY FINDINGS TABLE:");
console.log(JSON.stringify(findings, null, 2));
if (findings.filter((f) => f.severity === "High").length > 0) {
process.exit(1);
}
}
runSecurityScan().catch((err) => {
console.error("Scanner error:", err);
process.exit(1);
});