148 lines
5.1 KiB
JavaScript
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("<script>")) {
|
|
// 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);
|
|
});
|