@iota-big3/sdk-security
Version:
Advanced security features including zero trust, quantum-safe crypto, and ML threat detection
843 lines • 36.4 kB
JavaScript
;
/**
* Web Application Scanner
* OWASP Top 10 vulnerability detection and web security testing
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebScanner = void 0;
const tslib_1 = require("tslib");
const crypto = tslib_1.__importStar(require("crypto"));
const events_1 = require("events");
const types_1 = require("../types");
class WebScanner extends events_1.EventEmitter {
constructor() {
super();
this.userAgent = 'IOTA-Security-Scanner/1.0';
this.xssPayloads = [
'<script>alert(1)</script>',
'"><script>alert(1)</script>',
'<img src=x onerror=alert(1)>',
'javascript:alert(1)',
'<svg onload=alert(1)>'
];
this.sqlPayloads = [
"' OR '1'='1",
"1' OR '1'='1'--",
"' UNION SELECT NULL--",
"1; DROP TABLE users--",
"' OR 1=1#"
];
this.pathTraversalPayloads = [
'../../../etc/passwd',
'..\\..\\..\\windows\\win.ini',
'....//....//....//etc/passwd',
'%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd'
];
}
/**
* Scan web application
*/
async scanWebApp(url, options = {}) {
const startTime = new Date();
const target = {
id: crypto.randomUUID(),
type: 'APPLICATION',
address: url
};
this.emit('web-scan:started', { url });
try {
// Crawl the application
const crawlResult = await this.crawlApplication(url, options);
// Check for vulnerabilities
const vulnerabilities = [];
// Check security headers
if (options.checkHeaders !== false) {
const headerVulns = this.checkSecurityHeaders(url, crawlResult.headers);
vulnerabilities.push(...headerVulns);
}
// Check cookies
if (options.checkCookies !== false) {
const cookieVulns = this.checkCookieSecurity(crawlResult.cookies);
vulnerabilities.push(...cookieVulns);
}
// Test for XSS
const xssVulns = await this.testXSS(crawlResult, target);
vulnerabilities.push(...xssVulns);
// Test for SQL injection
const sqlVulns = await this.testSQLInjection(crawlResult, target);
vulnerabilities.push(...sqlVulns);
// Test for path traversal
const pathVulns = await this.testPathTraversal(crawlResult.urls, target);
vulnerabilities.push(...pathVulns);
// Test for insecure direct object references
const idorVulns = await this.testIDOR(crawlResult.urls, target);
vulnerabilities.push(...idorVulns);
// Test for CSRF
const csrfVulns = this.testCSRF(crawlResult.forms, target);
vulnerabilities.push(...csrfVulns);
// Test for XXE
const xxeVulns = await this.testXXE(crawlResult.forms, target);
vulnerabilities.push(...xxeVulns);
// Test authentication
const authVulns = await this.testAuthentication(url, target);
vulnerabilities.push(...authVulns);
const result = {
target,
startTime,
endTime: new Date(),
vulnerabilities,
crawledUrls: Array.from(crawlResult.urls),
forms: crawlResult.forms,
cookies: crawlResult.cookies,
headers: Object.fromEntries(crawlResult.headers)
};
this.emit('web-scan:completed', { url, vulnerabilities: vulnerabilities.length });
return result;
}
catch (error) {
this.emit('web-scan:failed', { url, error });
throw error;
}
}
/**
* Private methods
*/
async crawlApplication(baseUrl, options) {
const visited = new Set();
const toVisit = [baseUrl];
const forms = [];
const cookies = [];
const headers = new Map();
const maxUrls = options.maxUrls || 100;
const depth = options.depth || 3;
while (toVisit.length > 0 && visited.size < maxUrls) {
const url = toVisit.shift();
if (visited.has(url))
continue;
visited.add(url);
this.emit('crawl:url', { url });
// Mock HTTP request
const response = await this.mockHttpRequest(url, 'GET', undefined, options.authCookie);
// Extract headers
if (visited.size === 1) {
Object.entries(response.headers).forEach(([k, v]) => headers.set(k, v));
}
// Extract cookies
response.cookies.forEach(c => {
if (!cookies.find(existing => existing.name === c.name)) {
cookies.push(c);
}
});
// Extract forms
forms.push(...response.forms);
// Extract links
const links = this.extractLinks(response.body, url);
const currentDepth = url.split('/').length - 3;
links.forEach(link => {
const linkDepth = link.split('/').length - 3;
if (!visited.has(link) && linkDepth <= currentDepth + depth) {
toVisit.push(link);
}
});
}
return {
urls: visited,
forms,
cookies,
headers
};
}
checkSecurityHeaders(url, headers) {
const vulnerabilities = [];
const baseTarget = { id: '', type: 'APPLICATION', address: url };
// Check for missing security headers
const requiredHeaders = [
{
name: 'X-Frame-Options',
recommended: 'DENY',
vulnerability: 'Clickjacking'
},
{
name: 'X-Content-Type-Options',
recommended: 'nosniff',
vulnerability: 'MIME Sniffing'
},
{
name: 'X-XSS-Protection',
recommended: '1; mode=block',
vulnerability: 'XSS'
},
{
name: 'Strict-Transport-Security',
recommended: 'max-age=31536000',
vulnerability: 'HTTPS Downgrade'
},
{
name: 'Content-Security-Policy',
recommended: 'present',
vulnerability: 'XSS/Data Injection'
}
];
requiredHeaders.forEach(({ name, recommended, vulnerability }) => {
const value = headers.get(name.toLowerCase());
if (!value) {
vulnerabilities.push({
id: crypto.randomUUID(),
title: `Missing Security Header: ${name}`,
description: `The ${name} header is not set, which may allow ${vulnerability} attacks`,
severity: types_1.VulnerabilitySeverity.MEDIUM,
category: types_1.VulnerabilityCategory.SECURITY_MISCONFIGURATION,
discoveredAt: new Date(),
discoveredBy: 'WebScanner',
testType: 'WEB_APPLICATION',
affectedTarget: baseTarget,
affectedComponent: 'HTTP Headers',
attackVector: types_1.AttackVector.NETWORK,
exploitComplexity: 'LOW',
privilegesRequired: 'NONE',
userInteraction: vulnerability === 'Clickjacking' ? 'REQUIRED' : 'NONE',
evidence: [{
type: types_1.EvidenceType.RESPONSE,
data: 'Header not present in response',
timestamp: new Date()
}],
exploitStatus: types_1.ExploitStatus.NOT_ATTEMPTED,
impact: {
confidentiality: types_1.ImpactLevel.LOW,
integrity: types_1.ImpactLevel.LOW,
availability: types_1.ImpactLevel.NONE
},
remediation: {
summary: `Add ${name} header`,
steps: [`Set ${name}: ${recommended}`],
effort: 'LOW',
priority: 'MEDIUM',
retestRequired: true
},
riskScore: 5,
likelihood: 'MEDIUM'
});
}
});
return vulnerabilities;
}
checkCookieSecurity(cookies) {
const vulnerabilities = [];
cookies.forEach(cookie => {
const issues = [];
if (!cookie.secure && cookie.name.toLowerCase().includes('session')) {
issues.push('Missing Secure flag on session cookie');
}
if (!cookie.httpOnly && cookie.name.toLowerCase().includes('session')) {
issues.push('Missing HttpOnly flag on session cookie');
}
if (!cookie.sameSite) {
issues.push('Missing SameSite attribute');
}
if (issues.length > 0) {
vulnerabilities.push({
id: crypto.randomUUID(),
title: `Insecure Cookie: ${cookie.name}`,
description: issues.join(', '),
severity: types_1.VulnerabilitySeverity.MEDIUM,
category: types_1.VulnerabilityCategory.SECURITY_MISCONFIGURATION,
discoveredAt: new Date(),
discoveredBy: 'WebScanner',
testType: 'WEB_APPLICATION',
affectedTarget: { id: '', type: 'APPLICATION', address: cookie.domain },
affectedComponent: `Cookie: ${cookie.name}`,
attackVector: types_1.AttackVector.NETWORK,
exploitComplexity: 'LOW',
privilegesRequired: 'NONE',
userInteraction: 'NONE',
evidence: [{
type: types_1.EvidenceType.RESPONSE,
data: `Cookie attributes: Secure=${cookie.secure}, HttpOnly=${cookie.httpOnly}, SameSite=${cookie.sameSite}`,
timestamp: new Date()
}],
exploitStatus: types_1.ExploitStatus.NOT_ATTEMPTED,
impact: {
confidentiality: types_1.ImpactLevel.HIGH,
integrity: types_1.ImpactLevel.LOW,
availability: types_1.ImpactLevel.NONE
},
remediation: {
summary: 'Secure cookie attributes',
steps: [
'Set Secure flag for HTTPS transmission only',
'Set HttpOnly flag to prevent JavaScript access',
'Set SameSite attribute to prevent CSRF'
],
effort: 'LOW',
priority: 'HIGH',
retestRequired: true
},
riskScore: 6,
likelihood: 'MEDIUM'
});
}
});
return vulnerabilities;
}
async testXSS(crawlResult, target) {
const vulnerabilities = [];
// Test forms for XSS
for (const form of crawlResult.forms) {
for (const input of form.inputs) {
if (input.type === 'text' || input.type === 'textarea') {
for (const payload of this.xssPayloads) {
const response = await this.submitForm(form, { [input.name]: payload });
if (response.body.includes(payload)) {
vulnerabilities.push(this.createXSSVulnerability(form.action, input.name, payload, target));
break; // One vulnerability per input is enough
}
}
}
}
}
// Test URL parameters
for (const url of crawlResult.urls) {
if (url.includes('?')) {
const [base, query] = url.split('?');
const params = new URLSearchParams(query);
for (const [param] of params) {
for (const payload of this.xssPayloads) {
params.set(param, payload);
const testUrl = `${base}?${params.toString()}`;
const response = await this.mockHttpRequest(testUrl, 'GET');
if (response.body.includes(payload)) {
vulnerabilities.push(this.createXSSVulnerability(url, param, payload, target));
break;
}
}
}
}
}
return vulnerabilities;
}
createXSSVulnerability(url, parameter, payload, target) {
return {
id: crypto.randomUUID(),
title: `Cross-Site Scripting (XSS) in ${parameter}`,
description: `The parameter '${parameter}' is vulnerable to XSS attacks. User input is not properly sanitized.`,
severity: types_1.VulnerabilitySeverity.HIGH,
category: types_1.VulnerabilityCategory.XSS,
cwe: 'CWE-79',
discoveredAt: new Date(),
discoveredBy: 'WebScanner',
testType: 'WEB_APPLICATION',
affectedTarget: target,
affectedComponent: url,
attackVector: types_1.AttackVector.NETWORK,
exploitComplexity: 'LOW',
privilegesRequired: 'NONE',
userInteraction: 'REQUIRED',
evidence: [
{
type: types_1.EvidenceType.REQUEST,
data: `Parameter: ${parameter}\nPayload: ${payload}`,
timestamp: new Date()
},
{
type: types_1.EvidenceType.RESPONSE,
data: 'Payload reflected in response without encoding',
timestamp: new Date()
}
],
exploitStatus: types_1.ExploitStatus.SUCCESSFUL,
exploitDetails: {
exploitCode: payload,
exploitSteps: [
`Send payload: ${payload}`,
'Payload executed in victim\'s browser'
],
toolsUsed: ['Manual injection'],
timeToExploit: 5,
reliability: 'HIGH'
},
impact: {
confidentiality: types_1.ImpactLevel.HIGH,
integrity: types_1.ImpactLevel.HIGH,
availability: types_1.ImpactLevel.NONE,
businessImpact: {
reputation: 'User trust compromised',
regulatory: 'Potential data breach'
}
},
remediation: {
summary: 'Implement proper input validation and output encoding',
steps: [
'Validate all user input on the server side',
'Encode output based on context (HTML, JavaScript, CSS)',
'Use Content Security Policy headers',
'Consider using a Web Application Firewall'
],
effort: 'MEDIUM',
priority: 'IMMEDIATE',
references: [
'https://owasp.org/www-community/attacks/xss/',
'https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html'
],
retestRequired: true
},
riskScore: 8,
likelihood: 'HIGH'
};
}
async testSQLInjection(crawlResult, target) {
const vulnerabilities = [];
// Test forms
for (const form of crawlResult.forms) {
for (const input of form.inputs) {
for (const payload of this.sqlPayloads) {
const response = await this.submitForm(form, { [input.name]: payload });
if (this.detectSQLError(response.body)) {
vulnerabilities.push(this.createSQLInjectionVulnerability(form.action, input.name, payload, response.body, target));
break;
}
}
}
}
return vulnerabilities;
}
detectSQLError(body) {
const errorPatterns = [
/SQL syntax.*MySQL/i,
/Warning.*mysql_/i,
/MySqlException/i,
/valid PostgreSQL result/i,
/PostgreSQL.*ERROR/i,
/ORA-\d{5}/,
/Microsoft.*ODBC.*SQL/i,
/SQLException/i,
/SQL Server.*Error/i
];
return errorPatterns.some(pattern => pattern.test(body));
}
createSQLInjectionVulnerability(url, parameter, payload, errorMessage, target) {
return {
id: crypto.randomUUID(),
title: `SQL Injection in ${parameter}`,
description: `The parameter '${parameter}' is vulnerable to SQL injection. Database errors are exposed.`,
severity: types_1.VulnerabilitySeverity.CRITICAL,
category: types_1.VulnerabilityCategory.INJECTION,
cwe: 'CWE-89',
discoveredAt: new Date(),
discoveredBy: 'WebScanner',
testType: 'WEB_APPLICATION',
affectedTarget: target,
affectedComponent: url,
attackVector: types_1.AttackVector.NETWORK,
exploitComplexity: 'LOW',
privilegesRequired: 'NONE',
userInteraction: 'NONE',
evidence: [
{
type: types_1.EvidenceType.REQUEST,
data: `Parameter: ${parameter}\nPayload: ${payload}`,
timestamp: new Date()
},
{
type: types_1.EvidenceType.RESPONSE,
data: errorMessage.substring(0, 200) + '...',
timestamp: new Date()
}
],
exploitStatus: types_1.ExploitStatus.SUCCESSFUL,
impact: {
confidentiality: types_1.ImpactLevel.CRITICAL,
integrity: types_1.ImpactLevel.CRITICAL,
availability: types_1.ImpactLevel.HIGH,
businessImpact: {
financial: 'Complete database compromise possible',
regulatory: 'Major data breach risk'
}
},
remediation: {
summary: 'Use parameterized queries',
steps: [
'Use prepared statements with parameterized queries',
'Validate and sanitize all user input',
'Apply least privilege to database accounts',
'Disable error reporting in production'
],
effort: 'HIGH',
priority: 'IMMEDIATE',
references: [
'https://owasp.org/www-community/attacks/SQL_Injection'
],
retestRequired: true
},
riskScore: 10,
likelihood: 'CRITICAL'
};
}
async testPathTraversal(urls, target) {
const vulnerabilities = [];
for (const url of urls) {
// Look for file parameters
if (url.includes('file=') || url.includes('path=') || url.includes('doc=')) {
for (const payload of this.pathTraversalPayloads) {
const testUrl = url.replace(/([?&](file|path|doc)=)[^&]*/, `$1${payload}`);
const response = await this.mockHttpRequest(testUrl, 'GET');
if (this.detectPathTraversalSuccess(response.body)) {
vulnerabilities.push(this.createPathTraversalVulnerability(url, payload, target));
break;
}
}
}
}
return vulnerabilities;
}
detectPathTraversalSuccess(body) {
const successPatterns = [
/root:.*:0:0/, // Unix passwd file
/\[boot loader\]/i, // Windows ini file
/\[fonts\]/i // Windows ini file
];
return successPatterns.some(pattern => pattern.test(body));
}
createPathTraversalVulnerability(url, payload, target) {
return {
id: crypto.randomUUID(),
title: 'Path Traversal Vulnerability',
description: 'The application is vulnerable to path traversal attacks, allowing access to files outside the web root.',
severity: types_1.VulnerabilitySeverity.HIGH,
category: types_1.VulnerabilityCategory.BROKEN_ACCESS_CONTROL,
cwe: 'CWE-22',
discoveredAt: new Date(),
discoveredBy: 'WebScanner',
testType: 'WEB_APPLICATION',
affectedTarget: target,
affectedComponent: url,
attackVector: types_1.AttackVector.NETWORK,
exploitComplexity: 'LOW',
privilegesRequired: 'NONE',
userInteraction: 'NONE',
evidence: [
{
type: types_1.EvidenceType.REQUEST,
data: `Payload: ${payload}`,
timestamp: new Date()
},
{
type: types_1.EvidenceType.RESPONSE,
data: 'System file contents exposed',
timestamp: new Date()
}
],
exploitStatus: types_1.ExploitStatus.SUCCESSFUL,
impact: {
confidentiality: types_1.ImpactLevel.HIGH,
integrity: types_1.ImpactLevel.NONE,
availability: types_1.ImpactLevel.NONE
},
remediation: {
summary: 'Implement proper file access controls',
steps: [
'Use a whitelist of allowed files',
'Validate file paths and reject traversal sequences',
'Use chroot jail or similar sandboxing',
'Run web server with minimal privileges'
],
effort: 'MEDIUM',
priority: 'IMMEDIATE',
retestRequired: true
},
riskScore: 8,
likelihood: 'HIGH'
};
}
async testIDOR(urls, target) {
const vulnerabilities = [];
// Look for numeric IDs in URLs
const idPattern = /[?&](id|user|account|order)=(\d+)/;
for (const url of urls) {
const match = url.match(idPattern);
if (match) {
const param = match[1];
const originalId = match[2];
const testId = String(parseInt(originalId) + 1);
const testUrl = url.replace(`${param}=${originalId}`, `${param}=${testId}`);
const response = await this.mockHttpRequest(testUrl, 'GET');
// Simple check - if we get data back, might be IDOR
if (response.statusCode === 200 && response.body.length > 100) {
vulnerabilities.push({
id: crypto.randomUUID(),
title: `Insecure Direct Object Reference in ${param}`,
description: 'The application exposes internal object references without proper authorization checks.',
severity: types_1.VulnerabilitySeverity.HIGH,
category: types_1.VulnerabilityCategory.BROKEN_ACCESS_CONTROL,
cwe: 'CWE-639',
discoveredAt: new Date(),
discoveredBy: 'WebScanner',
testType: 'WEB_APPLICATION',
affectedTarget: target,
affectedComponent: url,
attackVector: types_1.AttackVector.NETWORK,
exploitComplexity: 'LOW',
privilegesRequired: 'LOW',
userInteraction: 'NONE',
evidence: [{
type: types_1.EvidenceType.REQUEST,
data: `Changed ${param} from ${originalId} to ${testId}`,
timestamp: new Date()
}],
exploitStatus: types_1.ExploitStatus.SUCCESSFUL,
impact: {
confidentiality: types_1.ImpactLevel.HIGH,
integrity: types_1.ImpactLevel.LOW,
availability: types_1.ImpactLevel.NONE
},
remediation: {
summary: 'Implement proper access controls',
steps: [
'Check user authorization for each object access',
'Use indirect object references (UUIDs)',
'Implement session-based access controls'
],
effort: 'MEDIUM',
priority: 'HIGH',
retestRequired: true
},
riskScore: 7,
likelihood: 'HIGH'
});
}
}
}
return vulnerabilities;
}
testCSRF(forms, target) {
const vulnerabilities = [];
forms.forEach(form => {
const hasToken = form.inputs.some(input => input.name.toLowerCase().includes('csrf') ||
input.name.toLowerCase().includes('token'));
if (!hasToken && form.method.toUpperCase() === 'POST') {
vulnerabilities.push({
id: crypto.randomUUID(),
title: `Missing CSRF Protection on ${form.action}`,
description: 'The form does not include CSRF tokens, making it vulnerable to cross-site request forgery.',
severity: types_1.VulnerabilitySeverity.MEDIUM,
category: types_1.VulnerabilityCategory.CSRF,
cwe: 'CWE-352',
discoveredAt: new Date(),
discoveredBy: 'WebScanner',
testType: 'WEB_APPLICATION',
affectedTarget: target,
affectedComponent: form.action,
attackVector: types_1.AttackVector.NETWORK,
exploitComplexity: 'LOW',
privilegesRequired: 'NONE',
userInteraction: 'REQUIRED',
evidence: [{
type: types_1.EvidenceType.CODE,
data: 'No CSRF token found in form',
timestamp: new Date()
}],
exploitStatus: types_1.ExploitStatus.NOT_ATTEMPTED,
impact: {
confidentiality: types_1.ImpactLevel.NONE,
integrity: types_1.ImpactLevel.HIGH,
availability: types_1.ImpactLevel.NONE
},
remediation: {
summary: 'Implement CSRF tokens',
steps: [
'Generate unique CSRF tokens per session',
'Include token in all state-changing forms',
'Validate token on server side',
'Consider using SameSite cookies'
],
effort: 'LOW',
priority: 'MEDIUM',
retestRequired: true
},
riskScore: 5,
likelihood: 'MEDIUM'
});
}
});
return vulnerabilities;
}
async testXXE(forms, target) {
const vulnerabilities = [];
// XXE payload
const xxePayload = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<data>&xxe;</data>`;
for (const form of forms) {
// Look for file upload or XML input
const hasFileInput = form.inputs.some(i => i.type === 'file');
const hasXMLEndpoint = form.action.includes('xml') || form.action.includes('upload');
if (hasFileInput || hasXMLEndpoint) {
const response = await this.submitForm(form, {}, xxePayload);
if (this.detectPathTraversalSuccess(response.body)) {
vulnerabilities.push({
id: crypto.randomUUID(),
title: 'XML External Entity (XXE) Injection',
description: 'The application processes XML input without disabling external entity resolution.',
severity: types_1.VulnerabilitySeverity.HIGH,
category: types_1.VulnerabilityCategory.XXE,
cwe: 'CWE-611',
discoveredAt: new Date(),
discoveredBy: 'WebScanner',
testType: 'WEB_APPLICATION',
affectedTarget: target,
affectedComponent: form.action,
attackVector: types_1.AttackVector.NETWORK,
exploitComplexity: 'LOW',
privilegesRequired: 'NONE',
userInteraction: 'NONE',
evidence: [{
type: types_1.EvidenceType.REQUEST,
data: xxePayload,
timestamp: new Date()
}],
exploitStatus: types_1.ExploitStatus.SUCCESSFUL,
impact: {
confidentiality: types_1.ImpactLevel.HIGH,
integrity: types_1.ImpactLevel.NONE,
availability: types_1.ImpactLevel.LOW
},
remediation: {
summary: 'Disable XML external entity processing',
steps: [
'Disable DTD processing entirely',
'Disable external entity resolution',
'Use less complex data formats (JSON)',
'Validate and sanitize XML input'
],
effort: 'LOW',
priority: 'HIGH',
retestRequired: true
},
riskScore: 7,
likelihood: 'MEDIUM'
});
break;
}
}
}
return vulnerabilities;
}
async testAuthentication(url, target) {
const vulnerabilities = [];
// Test for common weak credentials
const weakCredentials = [
{ username: 'admin', password: 'admin' },
{ username: 'admin', password: 'password' },
{ username: 'admin', password: '123456' },
{ username: 'test', password: 'test' }
];
// Mock authentication test
for (const creds of weakCredentials) {
const response = await this.mockHttpRequest(`${url}/login`, 'POST', creds);
if (response.statusCode === 200 || response.statusCode === 302) {
vulnerabilities.push({
id: crypto.randomUUID(),
title: 'Weak Default Credentials',
description: `The application accepts weak credentials: ${creds.username}/${creds.password}`,
severity: types_1.VulnerabilitySeverity.CRITICAL,
category: types_1.VulnerabilityCategory.BROKEN_AUTHENTICATION,
cwe: 'CWE-798',
discoveredAt: new Date(),
discoveredBy: 'WebScanner',
testType: 'WEB_APPLICATION',
affectedTarget: target,
affectedComponent: `${url}/login`,
attackVector: types_1.AttackVector.NETWORK,
exploitComplexity: 'LOW',
privilegesRequired: 'NONE',
userInteraction: 'NONE',
evidence: [{
type: types_1.EvidenceType.REQUEST,
data: `Credentials: ${creds.username}/${creds.password}`,
timestamp: new Date()
}],
exploitStatus: types_1.ExploitStatus.SUCCESSFUL,
impact: {
confidentiality: types_1.ImpactLevel.CRITICAL,
integrity: types_1.ImpactLevel.CRITICAL,
availability: types_1.ImpactLevel.HIGH
},
remediation: {
summary: 'Enforce strong password policy',
steps: [
'Remove all default credentials',
'Enforce strong password requirements',
'Implement account lockout policies',
'Use multi-factor authentication'
],
effort: 'MEDIUM',
priority: 'IMMEDIATE',
retestRequired: true
},
riskScore: 10,
likelihood: 'CRITICAL'
});
break;
}
}
return vulnerabilities;
}
// Helper methods
extractLinks(html, baseUrl) {
const links = [];
const linkRegex = /href="([^"]+)"/g;
let match;
while ((match = linkRegex.exec(html)) !== null) {
const link = match[1];
if (!link.startsWith('http')) {
// Relative link
const url = new URL(baseUrl);
links.push(url.origin + link);
}
else if (link.startsWith(new URL(baseUrl).origin)) {
// Same origin
links.push(link);
}
}
return links;
}
async submitForm(form, data, rawBody) {
return this.mockHttpRequest(form.action, form.method, data, undefined, rawBody);
}
async mockHttpRequest(url, method, data, authCookie, rawBody) {
// Mock implementation
const mockForms = [];
// Add some forms for certain URLs
if (url.includes('contact') || url.includes('login')) {
mockForms.push({
action: url,
method: 'POST',
inputs: [
{ name: 'username', type: 'text' },
{ name: 'password', type: 'password' },
{ name: 'submit', type: 'submit' }
]
});
}
return {
statusCode: 200,
headers: {
'content-type': 'text/html',
'server': 'Apache/2.4.41'
},
body: rawBody || '<html><body>Mock response</body></html>',
cookies: [
{
name: 'sessionid',
value: 'abc123',
domain: new URL(url).hostname,
path: '/',
secure: false,
httpOnly: false
}
],
forms: mockForms
};
}
}
exports.WebScanner = WebScanner;
//# sourceMappingURL=web-scanner.js.map