"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SecurityAnalyzerService = void 0; const axios_1 = __importDefault(require("axios")); const url_1 = require("url"); const logger_1 = require("../lib/logger"); class SecurityAnalyzerService { async analyzeSecurity(url, redirectChain) { logger_1.logger.info(`Starting security analysis for: ${url}`); const result = { url, flags: { safeBrowsingStatus: 'unknown', mixedContent: 'NONE', httpsToHttp: false, securityHeaders: { score: 0 }, weakCiphers: false, openRedirects: false, }, mixedContentAnalysis: { status: 'none', insecureResources: [], httpsToHttpRedirect: false, }, safeBrowsing: { status: 'unknown' }, vulnerabilities: [], recommendations: [], securityScore: 0, }; try { if (redirectChain && redirectChain.length > 0) { this.analyzeRedirectChainSecurity(redirectChain, result); } const headersAnalysis = await this.analyzeSecurityHeaders(url); result.flags.securityHeaders = headersAnalysis; const mixedContentAnalysis = await this.analyzeMixedContent(url); result.mixedContentAnalysis = mixedContentAnalysis; result.flags.mixedContent = mixedContentAnalysis.status === 'none' ? 'NONE' : mixedContentAnalysis.status === 'final_to_http' ? 'FINAL_TO_HTTP' : 'PRESENT'; result.flags.httpsToHttp = mixedContentAnalysis.httpsToHttpRedirect; const safeBrowsingResult = await this.checkSafeBrowsing(url); result.safeBrowsing = safeBrowsingResult; result.flags.safeBrowsingStatus = safeBrowsingResult.status; this.generateSecurityRecommendations(result); logger_1.logger.info(`Security analysis completed for: ${url}`, { score: result.securityScore, safeBrowsing: result.safeBrowsing.status, mixedContent: result.flags.mixedContent, headersScore: result.flags.securityHeaders.score }); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown security analysis error'; result.vulnerabilities.push(`Security analysis failed: ${errorMessage}`); logger_1.logger.error(`Security analysis failed for ${url}:`, error); } return result; } analyzeRedirectChainSecurity(redirectChain, result) { for (let i = 0; i < redirectChain.length - 1; i++) { const currentUrl = redirectChain[i]; const nextUrl = redirectChain[i + 1]; try { const current = new url_1.URL(currentUrl); const next = new url_1.URL(nextUrl); if (current.protocol === 'https:' && next.protocol === 'http:') { result.flags.httpsToHttp = true; result.vulnerabilities.push(`Insecure redirect: HTTPS to HTTP (${currentUrl} → ${nextUrl})`); } if (current.hostname !== next.hostname) { if (this.isOpenRedirectVulnerable(currentUrl, nextUrl)) { result.flags.openRedirects = true; result.vulnerabilities.push(`Potential open redirect vulnerability: ${currentUrl} → ${nextUrl}`); } } } catch (error) { logger_1.logger.warn(`Failed to parse URLs in redirect chain: ${currentUrl} → ${nextUrl}`); } } } isOpenRedirectVulnerable(fromUrl, toUrl) { try { const from = new url_1.URL(fromUrl); const to = new url_1.URL(toUrl); if (from.hostname !== to.hostname) { const urlParams = new URLSearchParams(from.search); for (const [key, value] of urlParams.entries()) { if (key.toLowerCase().includes('url') || key.toLowerCase().includes('redirect') || key.toLowerCase().includes('return')) { if (value.includes(to.hostname)) { return true; } } } } return false; } catch { return false; } } async analyzeSecurityHeaders(url) { try { const response = await axios_1.default.head(url, { timeout: 5000, headers: { 'User-Agent': 'RedirectIntelligence-Bot/2.0 (Security Analysis)', }, }); const headers = response.headers; let score = 0; const analysis = { score: 0, }; if (headers['strict-transport-security']) { analysis.strictTransportSecurity = headers['strict-transport-security']; score += 20; } if (headers['content-security-policy']) { analysis.contentSecurityPolicy = headers['content-security-policy']; score += 25; } if (headers['x-frame-options']) { analysis.xFrameOptions = headers['x-frame-options']; score += 15; } if (headers['x-content-type-options']) { analysis.xContentTypeOptions = headers['x-content-type-options']; score += 10; } if (headers['referrer-policy']) { analysis.referrerPolicy = headers['referrer-policy']; score += 10; } if (headers['permissions-policy'] || headers['feature-policy']) { analysis.permissionsPolicy = headers['permissions-policy'] || headers['feature-policy']; score += 10; } analysis.score = score; return analysis; } catch (error) { logger_1.logger.warn(`Failed to analyze security headers for ${url}:`, error); return { score: 0 }; } } async analyzeMixedContent(url) { const result = { status: 'none', insecureResources: [], httpsToHttpRedirect: false, }; try { const parsedUrl = new url_1.URL(url); if (parsedUrl.protocol === 'http:') { result.status = 'final_to_http'; result.httpsToHttpRedirect = true; return result; } if (parsedUrl.protocol === 'https:') { const mixedContentCheck = await this.checkPageMixedContent(url); if (mixedContentCheck.length > 0) { result.status = 'present'; result.insecureResources = mixedContentCheck; } } } catch (error) { logger_1.logger.warn(`Mixed content analysis failed for ${url}:`, error); } return result; } async checkPageMixedContent(url) { try { const response = await axios_1.default.get(url, { timeout: 10000, headers: { 'User-Agent': 'RedirectIntelligence-Bot/2.0 (Mixed Content Analysis)', }, maxContentLength: 512 * 1024, }); const html = response.data; const insecureResources = []; const httpResourceRegex = /(?:src|href|action)=['"]http:\/\/[^'"]+['"]|url\(http:\/\/[^)]+\)/gi; let match; while ((match = httpResourceRegex.exec(html)) !== null) { const resource = match[0]; const urlMatch = resource.match(/http:\/\/[^'")\s]+/); if (urlMatch) { insecureResources.push(urlMatch[0]); } } return [...new Set(insecureResources)]; } catch (error) { logger_1.logger.warn(`Failed to check mixed content for ${url}:`, error); return []; } } async checkSafeBrowsing(url) { try { const parsedUrl = new url_1.URL(url); const hostname = parsedUrl.hostname.toLowerCase(); const suspiciousPatterns = [ /phishing/i, /malware/i, /virus/i, /hack/i, /suspicious/i, ]; for (const pattern of suspiciousPatterns) { if (pattern.test(hostname)) { return { status: 'phishing', details: 'Suspicious hostname pattern detected', }; } } const suspiciousTlds = ['.tk', '.ml', '.ga', '.cf']; const tld = hostname.substring(hostname.lastIndexOf('.')); if (suspiciousTlds.includes(tld)) { return { status: 'unwanted_software', details: 'Suspicious top-level domain', }; } return { status: 'safe' }; } catch (error) { logger_1.logger.warn(`Safe browsing check failed for ${url}:`, error); return { status: 'error', details: 'Safe browsing check failed', }; } } generateSecurityRecommendations(result) { let score = 100; if (result.safeBrowsing.status === 'malware' || result.safeBrowsing.status === 'phishing') { result.vulnerabilities.push(`Site flagged as ${result.safeBrowsing.status}`); result.recommendations.push('Immediately investigate and resolve security issues'); score -= 50; } else if (result.safeBrowsing.status === 'unwanted_software') { result.vulnerabilities.push('Site may contain unwanted software'); result.recommendations.push('Review site content and remove unwanted software'); score -= 30; } if (result.flags.httpsToHttp) { result.vulnerabilities.push('HTTPS to HTTP downgrade detected'); result.recommendations.push('Ensure all redirects maintain HTTPS encryption'); score -= 25; } if (result.mixedContentAnalysis.insecureResources.length > 0) { result.vulnerabilities.push(`${result.mixedContentAnalysis.insecureResources.length} insecure resources found`); result.recommendations.push('Update all HTTP resources to use HTTPS'); score -= 15; } const headersScore = result.flags.securityHeaders.score; if (headersScore < 50) { result.recommendations.push('Implement security headers (CSP, HSTS, X-Frame-Options)'); score -= 20; } else if (headersScore < 80) { result.recommendations.push('Add additional security headers for better protection'); score -= 10; } if (result.flags.openRedirects) { result.vulnerabilities.push('Potential open redirect vulnerability detected'); result.recommendations.push('Validate and restrict redirect destinations'); score -= 20; } score = Math.min(score, score + (headersScore * 0.2)); result.securityScore = Math.max(0, score); if (result.securityScore >= 90) { result.recommendations.push('Excellent security posture!'); } else if (result.securityScore >= 70) { result.recommendations.push('Good security with minor improvements possible'); } else if (result.securityScore >= 50) { result.recommendations.push('Security needs improvement for better protection'); } else { result.recommendations.push('Security requires immediate attention'); } } async quickSecurityCheck(url) { try { const analysis = await this.analyzeSecurity(url); return { httpsToHttp: analysis.flags.httpsToHttp, safeBrowsingStatus: analysis.flags.safeBrowsingStatus, hasSecurityHeaders: analysis.flags.securityHeaders.score > 0, }; } catch (error) { logger_1.logger.warn(`Quick security check failed for ${url}:`, error); return { httpsToHttp: false, safeBrowsingStatus: 'unknown', hasSecurityHeaders: false, }; } } } exports.SecurityAnalyzerService = SecurityAnalyzerService; //# sourceMappingURL=security-analyzer.service.js.map