directory-scanner.tsā¢30.4 kB
import { exec } from 'child_process';
import { promisify } from 'util';
import { ScanResult } from './recon.js';
const execAsync = promisify(exec);
export interface DirectoryResult {
url: string;
status_code: number;
content_length: number;
content_type?: string;
redirect_location?: string;
response_time: number;
is_directory: boolean;
is_sensitive: boolean;
risk_level: 'info' | 'low' | 'medium' | 'high' | 'critical';
description: string;
recommendations: string[];
}
export interface DirectoryScanConfiguration {
tool: 'dirb' | 'dirsearch' | 'gobuster' | 'feroxbuster';
wordlist?: string;
extensions?: string[];
threads: number;
timeout: number;
recursive: boolean;
max_depth: number;
status_codes: number[];
user_agent?: string;
headers?: Record<string, string>;
proxy?: string;
delay?: number;
}
export class DirectoryScannerEngine {
async scanDirectories(target: string, config: Partial<DirectoryScanConfiguration> = {}): Promise<ScanResult> {
try {
const defaultConfig: DirectoryScanConfiguration = {
tool: 'dirsearch',
wordlist: this.getDefaultWordlist(),
extensions: ['php', 'asp', 'aspx', 'jsp', 'html', 'htm', 'txt', 'xml', 'json', 'js', 'css'],
threads: 30,
timeout: 10,
recursive: true,
max_depth: 3,
status_codes: [200, 201, 204, 301, 302, 307, 308, 401, 403, 405, 500],
user_agent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
delay: 0,
...config
};
console.error(`š Directory scanning ${target} with ${defaultConfig.tool}`);
let results: DirectoryResult[] = [];
switch (defaultConfig.tool) {
case 'dirb':
results = await this.runDirb(target, defaultConfig);
break;
case 'dirsearch':
results = await this.runDirsearch(target, defaultConfig);
break;
case 'gobuster':
results = await this.runGobuster(target, defaultConfig);
break;
case 'feroxbuster':
results = await this.runFeroxbuster(target, defaultConfig);
break;
default:
throw new Error(`Unsupported tool: ${defaultConfig.tool}`);
}
// Analyze and categorize results
const analyzedResults = this.analyzeResults(results);
const sensitiveFiles = analyzedResults.filter(r => r.is_sensitive);
const accessibleDirs = analyzedResults.filter(r => r.is_directory && r.status_code === 200);
const highRiskFindings = analyzedResults.filter(r => r.risk_level === 'high' || r.risk_level === 'critical');
return {
target,
timestamp: new Date().toISOString(),
tool: 'directory_scanner',
results: {
scan_tool: defaultConfig.tool,
total_discovered: results.length,
accessible_directories: accessibleDirs.length,
sensitive_files: sensitiveFiles.length,
high_risk_findings: highRiskFindings.length,
status_code_breakdown: this.groupByStatusCode(results),
risk_level_breakdown: this.groupByRiskLevel(analyzedResults),
discovered_paths: analyzedResults,
security_recommendations: this.generateSecurityRecommendations(analyzedResults)
},
status: 'success'
};
} catch (error) {
return {
target,
timestamp: new Date().toISOString(),
tool: 'directory_scanner',
results: {},
status: 'error',
error: error instanceof Error ? error.message : String(error)
};
}
}
async advancedDirectoryScan(target: string): Promise<ScanResult> {
try {
console.error(`š Advanced multi-tool directory scanning for ${target}`);
const allResults: DirectoryResult[] = [];
// Phase 1: Quick discovery with common wordlist
console.error(' Phase 1: Quick discovery scan');
const quickResults = await this.scanDirectories(target, {
tool: 'dirsearch',
extensions: ['php', 'html', 'asp', 'jsp'],
threads: 50,
recursive: false
});
if (quickResults.status === 'success') {
allResults.push(...(quickResults.results.discovered_paths || []));
}
// Phase 2: Technology-specific scanning
console.error(' Phase 2: Technology-specific scanning');
const techResults = await this.technologySpecificScan(target);
allResults.push(...techResults);
// Phase 3: Backup and sensitive file discovery
console.error(' Phase 3: Sensitive file discovery');
const sensitiveResults = await this.sensitiveFileScan(target);
allResults.push(...sensitiveResults);
// Phase 4: API endpoint discovery
console.error(' Phase 4: API endpoint discovery');
const apiResults = await this.apiEndpointScan(target);
allResults.push(...apiResults);
// Deduplicate and analyze final results
const uniqueResults = this.deduplicateResults(allResults);
const finalAnalyzed = this.analyzeResults(uniqueResults);
return {
target,
timestamp: new Date().toISOString(),
tool: 'advanced_directory_scanner',
results: {
total_phases: 4,
total_discovered: finalAnalyzed.length,
unique_paths: finalAnalyzed.length,
critical_findings: finalAnalyzed.filter(r => r.risk_level === 'critical').length,
high_risk_findings: finalAnalyzed.filter(r => r.risk_level === 'high').length,
sensitive_files: finalAnalyzed.filter(r => r.is_sensitive).length,
discovered_paths: finalAnalyzed,
attack_surface_summary: this.generateAttackSurfaceSummary(finalAnalyzed),
prioritized_recommendations: this.generatePrioritizedRecommendations(finalAnalyzed)
},
status: 'success'
};
} catch (error) {
return {
target,
timestamp: new Date().toISOString(),
tool: 'advanced_directory_scanner',
results: {},
status: 'error',
error: error instanceof Error ? error.message : String(error)
};
}
}
private async runDirb(target: string, config: DirectoryScanConfiguration): Promise<DirectoryResult[]> {
try {
// Check if dirb is installed
try {
await execAsync('dirb -h', { timeout: 5000 });
} catch {
console.warn('dirb not found, skipping dirb scan');
return [];
}
const extensions = config.extensions?.map(ext => `.${ext}`).join(',') || '';
const command = `dirb ${target} ${config.wordlist} -X ${extensions} -w -r`;
console.error(`Executing: ${command}`);
const { stdout } = await execAsync(command, {
timeout: 300000, // 5 minutes
maxBuffer: 1024 * 1024 * 10 // 10MB
});
return this.parseDirbOutput(stdout, target);
} catch (error) {
console.error('Dirb execution error:', error);
return [];
}
}
private async runDirsearch(target: string, config: DirectoryScanConfiguration): Promise<DirectoryResult[]> {
try {
// Check if dirsearch is installed
try {
await execAsync('dirsearch -h', { timeout: 5000 });
} catch {
console.warn('dirsearch not found, skipping dirsearch scan');
return [];
}
const extensions = config.extensions?.join(',') || 'php,asp,aspx,jsp,html';
const statusCodes = config.status_codes.join(',');
let command = `dirsearch -u ${target} -e ${extensions} -t ${config.threads} --timeout ${config.timeout}`;
command += ` --include-status ${statusCodes}`;
if (config.recursive) {
command += ` -r --max-recursion-depth ${config.max_depth}`;
}
if (config.wordlist) {
command += ` -w ${config.wordlist}`;
}
if (config.user_agent) {
command += ` --user-agent "${config.user_agent}"`;
}
command += ' --format simple --quiet-mode';
console.error(`Executing: ${command}`);
const { stdout } = await execAsync(command, {
timeout: 600000, // 10 minutes
maxBuffer: 1024 * 1024 * 20 // 20MB
});
return this.parseDirsearchOutput(stdout, target);
} catch (error) {
console.error('Dirsearch execution error:', error);
return [];
}
}
private async runGobuster(target: string, config: DirectoryScanConfiguration): Promise<DirectoryResult[]> {
try {
// Check if gobuster is installed
try {
await execAsync('gobuster version', { timeout: 5000 });
} catch {
console.warn('gobuster not found, skipping gobuster scan');
return [];
}
const extensions = config.extensions?.join(',') || 'php,asp,aspx,jsp,html';
const statusCodes = config.status_codes.join(',');
let command = `gobuster dir -u ${target} -w ${config.wordlist} -x ${extensions}`;
command += ` -t ${config.threads} --timeout ${config.timeout}s -s ${statusCodes}`;
command += ' --no-error --quiet';
if (config.user_agent) {
command += ` -a "${config.user_agent}"`;
}
console.error(`Executing: ${command}`);
const { stdout } = await execAsync(command, {
timeout: 600000, // 10 minutes
maxBuffer: 1024 * 1024 * 10 // 10MB
});
return this.parseGobusterOutput(stdout, target);
} catch (error) {
console.error('Gobuster execution error:', error);
return [];
}
}
private async runFeroxbuster(target: string, config: DirectoryScanConfiguration): Promise<DirectoryResult[]> {
try {
// Check if feroxbuster is installed
try {
await execAsync('feroxbuster --version', { timeout: 5000 });
} catch {
console.warn('feroxbuster not found, skipping feroxbuster scan');
return [];
}
const extensions = config.extensions?.join(',') || 'php,asp,aspx,jsp,html';
const statusCodes = config.status_codes.join(',');
let command = `feroxbuster -u ${target} -w ${config.wordlist} -x ${extensions}`;
command += ` -t ${config.threads} -T ${config.timeout} -s ${statusCodes}`;
command += ' --silent --no-state';
if (config.recursive) {
command += ` -d ${config.max_depth}`;
} else {
command += ' --no-recursion';
}
if (config.user_agent) {
command += ` -a "${config.user_agent}"`;
}
console.error(`Executing: ${command}`);
const { stdout } = await execAsync(command, {
timeout: 600000, // 10 minutes
maxBuffer: 1024 * 1024 * 20 // 20MB
});
return this.parseFeroxbusterOutput(stdout, target);
} catch (error) {
console.error('Feroxbuster execution error:', error);
return [];
}
}
private async technologySpecificScan(target: string): Promise<DirectoryResult[]> {
const results: DirectoryResult[] = [];
// Technology-specific wordlists and paths
const techPaths = {
wordpress: [
'wp-admin', 'wp-content', 'wp-includes', 'wp-config.php',
'wp-content/uploads', 'wp-content/themes', 'wp-content/plugins',
'xmlrpc.php', 'readme.html', 'license.txt'
],
drupal: [
'sites/default', 'modules', 'themes', 'core',
'sites/default/settings.php', 'update.php', 'install.php'
],
joomla: [
'administrator', 'components', 'modules', 'plugins',
'configuration.php', 'htaccess.txt'
],
php: [
'phpinfo.php', 'info.php', 'test.php', 'config.php',
'database.php', 'db.php', 'connect.php'
],
asp: [
'web.config', 'global.asax', 'bin', 'App_Data',
'default.aspx', 'login.aspx', 'admin.aspx'
],
java: [
'WEB-INF', 'META-INF', 'classes', 'lib',
'web.xml', 'struts.xml', 'spring'
]
};
// Test each technology's common paths
for (const [tech, paths] of Object.entries(techPaths)) {
for (const path of paths.slice(0, 10)) { // Limit paths per technology
try {
const testUrl = `${target.replace(/\/$/, '')}/${path}`;
const result = await this.testSinglePath(testUrl);
if (result && (result.status_code === 200 || result.status_code === 403)) {
results.push({
...result,
description: `${tech.toUpperCase()} technology file/directory`,
is_sensitive: this.isSensitivePath(path),
risk_level: this.calculateRiskLevel(path, result.status_code)
});
}
// Rate limiting
await this.sleep(50);
} catch {
// Continue with next path
}
}
}
return results;
}
private async sensitiveFileScan(target: string): Promise<DirectoryResult[]> {
const results: DirectoryResult[] = [];
const sensitivePaths = [
// Configuration files
'.env', '.env.local', '.env.production',
'config.php', 'config.ini', 'configuration.php',
'settings.php', 'config.json', 'config.yml',
// Database files
'database.sql', 'backup.sql', 'dump.sql',
'db.sqlite', 'database.db', 'app.db',
// Backup files
'backup.zip', 'backup.tar.gz', 'backup.7z',
'site.zip', 'www.zip', 'web.zip',
'backup.rar', 'files.zip',
// Version control
'.git/config', '.git/HEAD', '.git/logs/HEAD',
'.svn/entries', '.hg/hgrc',
// Log files
'error.log', 'access.log', 'app.log',
'debug.log', 'application.log',
// Development files
'test.php', 'debug.php', 'dev.php',
'phpinfo.php', 'info.php',
// API keys and secrets
'api_keys.txt', 'secrets.txt', 'credentials.txt',
'keys.txt', 'passwords.txt',
// Server files
'.htaccess', '.htpasswd', 'web.config',
'robots.txt', 'sitemap.xml',
// Admin panels
'admin.php', 'administrator.php', 'manage.php',
'control.php', 'panel.php'
];
for (const path of sensitivePaths) {
try {
const testUrl = `${target.replace(/\/$/, '')}/${path}`;
const result = await this.testSinglePath(testUrl);
if (result && result.status_code !== 404) {
results.push({
...result,
description: `Sensitive file: ${path}`,
is_sensitive: true,
risk_level: this.calculateSensitiveFileRisk(path, result.status_code)
});
}
// Rate limiting
await this.sleep(100);
} catch {
// Continue with next path
}
}
return results;
}
private async apiEndpointScan(target: string): Promise<DirectoryResult[]> {
const results: DirectoryResult[] = [];
const apiPaths = [
// Common API paths
'api', 'api/v1', 'api/v2', 'api/v3',
'rest', 'rest/v1', 'rest/v2',
'graphql', 'graphiql',
// Documentation
'swagger', 'swagger-ui', 'swagger.json',
'openapi.json', 'api-docs', 'docs',
// Admin APIs
'admin/api', 'admin/rest',
'management/api', 'internal/api',
// Mobile APIs
'mobile/api', 'app/api',
'm/api', 'mobile/rest',
// Versioned endpoints
'v1', 'v2', 'v3', 'latest',
'beta', 'dev', 'test'
];
for (const path of apiPaths) {
try {
const testUrl = `${target.replace(/\/$/, '')}/${path}`;
const result = await this.testSinglePath(testUrl);
if (result && (result.status_code === 200 || result.status_code === 401 || result.status_code === 403)) {
results.push({
...result,
description: `API endpoint: ${path}`,
is_sensitive: path.includes('admin') || path.includes('internal'),
risk_level: this.calculateAPIRisk(path, result.status_code)
});
}
// Rate limiting
await this.sleep(75);
} catch {
// Continue with next path
}
}
return results;
}
private async testSinglePath(url: string): Promise<DirectoryResult | null> {
const axios = require('axios');
try {
const startTime = Date.now();
const response = await axios.get(url, {
timeout: 10000,
maxRedirects: 5,
validateStatus: () => true, // Accept all status codes
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
});
const responseTime = Date.now() - startTime;
return {
url,
status_code: response.status,
content_length: response.data?.length || response.headers['content-length'] || 0,
content_type: response.headers['content-type'],
redirect_location: response.headers['location'],
response_time: responseTime,
is_directory: this.isDirectory(response),
is_sensitive: false,
risk_level: 'info',
description: '',
recommendations: []
};
} catch (error: any) {
if (error.response) {
return {
url,
status_code: error.response.status,
content_length: 0,
response_time: 0,
is_directory: false,
is_sensitive: false,
risk_level: 'info',
description: '',
recommendations: []
};
}
return null;
}
}
private parseDirbOutput(output: string, target: string): DirectoryResult[] {
const results: DirectoryResult[] = [];
const lines = output.split('\n');
for (const line of lines) {
if (line.includes('==> DIRECTORY:') || line.includes('+ ')) {
const match = line.match(/\+ ([^\s]+) \(CODE:(\d+)\|SIZE:(\d+)\)/);
if (match) {
const url = match[1];
const statusCode = parseInt(match[2]);
const size = parseInt(match[3]);
results.push({
url,
status_code: statusCode,
content_length: size,
response_time: 0,
is_directory: line.includes('==> DIRECTORY:'),
is_sensitive: false,
risk_level: 'info',
description: '',
recommendations: []
});
}
}
}
return results;
}
private parseDirsearchOutput(output: string, target: string): DirectoryResult[] {
const results: DirectoryResult[] = [];
const lines = output.split('\n');
for (const line of lines) {
// Parse dirsearch output format
const match = line.match(/(\d+)\s+(\d+)B?\s+([^\s]+)/);
if (match) {
const statusCode = parseInt(match[1]);
const size = parseInt(match[2]);
const path = match[3];
results.push({
url: `${target.replace(/\/$/, '')}${path}`,
status_code: statusCode,
content_length: size,
response_time: 0,
is_directory: path.endsWith('/'),
is_sensitive: false,
risk_level: 'info',
description: '',
recommendations: []
});
}
}
return results;
}
private parseGobusterOutput(output: string, target: string): DirectoryResult[] {
const results: DirectoryResult[] = [];
const lines = output.split('\n');
for (const line of lines) {
// Parse gobuster output format
const match = line.match(/([^\s]+)\s+\(Status:\s*(\d+)\)\s*\[Size:\s*(\d+)\]/);
if (match) {
const path = match[1];
const statusCode = parseInt(match[2]);
const size = parseInt(match[3]);
results.push({
url: `${target.replace(/\/$/, '')}${path}`,
status_code: statusCode,
content_length: size,
response_time: 0,
is_directory: path.endsWith('/'),
is_sensitive: false,
risk_level: 'info',
description: '',
recommendations: []
});
}
}
return results;
}
private parseFeroxbusterOutput(output: string, target: string): DirectoryResult[] {
const results: DirectoryResult[] = [];
const lines = output.split('\n');
for (const line of lines) {
// Parse feroxbuster output format
const match = line.match(/(\d+)\s+(\d+)c\s+([^\s]+)/);
if (match) {
const statusCode = parseInt(match[1]);
const size = parseInt(match[2]);
const url = match[3];
results.push({
url,
status_code: statusCode,
content_length: size,
response_time: 0,
is_directory: url.endsWith('/'),
is_sensitive: false,
risk_level: 'info',
description: '',
recommendations: []
});
}
}
return results;
}
private analyzeResults(results: DirectoryResult[]): DirectoryResult[] {
return results.map(result => {
// Analyze each result for security implications
result.is_sensitive = this.isSensitivePath(result.url);
result.risk_level = this.calculateRiskLevel(result.url, result.status_code);
result.description = this.generateDescription(result);
result.recommendations = this.generateRecommendations(result);
return result;
});
}
private isSensitivePath(path: string): boolean {
const sensitivePatterns = [
'.env', '.git', '.svn', 'config', 'backup',
'admin', 'database', 'log', 'debug', 'test',
'phpinfo', 'api', 'swagger', 'internal'
];
return sensitivePatterns.some(pattern =>
path.toLowerCase().includes(pattern)
);
}
private calculateRiskLevel(path: string, statusCode: number): 'info' | 'low' | 'medium' | 'high' | 'critical' {
if (statusCode === 404) return 'info';
const pathLower = path.toLowerCase();
// Critical risks
if (pathLower.includes('.env') || pathLower.includes('.git') ||
pathLower.includes('database') || pathLower.includes('backup')) {
return statusCode === 200 ? 'critical' : 'high';
}
// High risks
if (pathLower.includes('admin') || pathLower.includes('config') ||
pathLower.includes('phpinfo') || pathLower.includes('debug')) {
return statusCode === 200 ? 'high' : 'medium';
}
// Medium risks
if (pathLower.includes('api') || pathLower.includes('test') ||
pathLower.includes('dev') || pathLower.includes('swagger')) {
return statusCode === 200 ? 'medium' : 'low';
}
return statusCode === 200 ? 'low' : 'info';
}
private calculateSensitiveFileRisk(path: string, statusCode: number): 'info' | 'low' | 'medium' | 'high' | 'critical' {
if (statusCode === 404) return 'info';
const pathLower = path.toLowerCase();
if (pathLower.includes('.env') || pathLower.includes('.git/config') ||
pathLower.includes('database.sql') || pathLower.includes('backup')) {
return 'critical';
}
if (pathLower.includes('config') || pathLower.includes('phpinfo') ||
pathLower.includes('.htpasswd') || pathLower.includes('web.config')) {
return 'high';
}
if (pathLower.includes('log') || pathLower.includes('debug') ||
pathLower.includes('test') || pathLower.includes('robots.txt')) {
return 'medium';
}
return 'low';
}
private calculateAPIRisk(path: string, statusCode: number): 'info' | 'low' | 'medium' | 'high' | 'critical' {
if (statusCode === 404) return 'info';
const pathLower = path.toLowerCase();
if (pathLower.includes('admin') || pathLower.includes('internal')) {
return statusCode === 200 ? 'high' : 'medium';
}
if (pathLower.includes('swagger') || pathLower.includes('graphql')) {
return statusCode === 200 ? 'medium' : 'low';
}
return statusCode === 200 ? 'low' : 'info';
}
private isDirectory(response: any): boolean {
const contentType = response.headers['content-type'] || '';
return contentType.includes('text/html') &&
(response.data?.includes('<title>Index of') ||
response.data?.includes('Directory listing'));
}
private generateDescription(result: DirectoryResult): string {
if (result.status_code === 200) {
return `Accessible ${result.is_directory ? 'directory' : 'file'}: ${result.url}`;
} else if (result.status_code === 403) {
return `Forbidden ${result.is_directory ? 'directory' : 'file'}: ${result.url}`;
} else if (result.status_code === 401) {
return `Authentication required for: ${result.url}`;
} else {
return `Found ${result.url} (Status: ${result.status_code})`;
}
}
private generateRecommendations(result: DirectoryResult): string[] {
const recommendations: string[] = [];
if (result.is_sensitive && result.status_code === 200) {
recommendations.push('Restrict access to this sensitive resource');
recommendations.push('Consider moving sensitive files outside web root');
}
if (result.url.toLowerCase().includes('admin') && result.status_code === 200) {
recommendations.push('Implement strong authentication for admin areas');
recommendations.push('Consider IP whitelisting for admin access');
}
if (result.url.toLowerCase().includes('backup')) {
recommendations.push('Remove backup files from public access');
recommendations.push('Store backups in secure, non-web accessible location');
}
if (result.url.toLowerCase().includes('api') && result.status_code === 200) {
recommendations.push('Implement proper API authentication');
recommendations.push('Consider rate limiting for API endpoints');
}
return recommendations;
}
private deduplicateResults(results: DirectoryResult[]): DirectoryResult[] {
const seen = new Set<string>();
return results.filter(result => {
if (seen.has(result.url)) {
return false;
}
seen.add(result.url);
return true;
});
}
private groupByStatusCode(results: DirectoryResult[]): Record<string, number> {
const groups: Record<string, number> = {};
for (const result of results) {
const code = result.status_code.toString();
groups[code] = (groups[code] || 0) + 1;
}
return groups;
}
private groupByRiskLevel(results: DirectoryResult[]): Record<string, number> {
const groups: Record<string, number> = {
critical: 0, high: 0, medium: 0, low: 0, info: 0
};
for (const result of results) {
groups[result.risk_level]++;
}
return groups;
}
private generateSecurityRecommendations(results: DirectoryResult[]): string[] {
const recommendations = new Set<string>();
const criticalFindings = results.filter(r => r.risk_level === 'critical');
const sensitiveFiles = results.filter(r => r.is_sensitive);
if (criticalFindings.length > 0) {
recommendations.add('Immediately secure or remove critical security exposures');
}
if (sensitiveFiles.length > 0) {
recommendations.add('Review and restrict access to sensitive files and directories');
}
if (results.some(r => r.url.includes('admin'))) {
recommendations.add('Implement strong authentication for administrative interfaces');
}
if (results.some(r => r.url.includes('backup'))) {
recommendations.add('Remove backup files from web-accessible locations');
}
if (results.some(r => r.url.includes('api'))) {
recommendations.add('Secure API endpoints with proper authentication and rate limiting');
}
recommendations.add('Implement proper access controls and directory browsing restrictions');
recommendations.add('Regular security assessments to identify new exposures');
return Array.from(recommendations);
}
private generateAttackSurfaceSummary(results: DirectoryResult[]): any {
return {
total_discovered_paths: results.length,
accessible_resources: results.filter(r => r.status_code === 200).length,
administrative_interfaces: results.filter(r => r.url.includes('admin')).length,
api_endpoints: results.filter(r => r.url.includes('api')).length,
configuration_files: results.filter(r => r.url.includes('config')).length,
backup_files: results.filter(r => r.url.includes('backup')).length,
development_files: results.filter(r => r.url.includes('test') || r.url.includes('dev')).length,
information_disclosure: results.filter(r => r.url.includes('phpinfo') || r.url.includes('debug')).length
};
}
private generatePrioritizedRecommendations(results: DirectoryResult[]): string[] {
const recommendations: string[] = [];
const criticalCount = results.filter(r => r.risk_level === 'critical').length;
const highCount = results.filter(r => r.risk_level === 'high').length;
if (criticalCount > 0) {
recommendations.push(`Priority 1: Address ${criticalCount} critical security exposures immediately`);
}
if (highCount > 0) {
recommendations.push(`Priority 2: Secure ${highCount} high-risk findings within 24 hours`);
}
recommendations.push('Priority 3: Implement comprehensive access controls');
recommendations.push('Priority 4: Regular security monitoring and assessment');
return recommendations;
}
private getDefaultWordlist(): string {
// Common wordlist locations
const wordlists = [
'/usr/share/wordlists/dirb/common.txt',
'/usr/share/wordlists/SecLists/Discovery/Web-Content/common.txt',
'/opt/wordlists/common.txt',
'/usr/share/dirb/wordlists/common.txt'
];
return wordlists[0]; // Default to dirb common wordlist
}
private async sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}