"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SSLAnalyzerService = void 0; const tls_1 = __importDefault(require("tls")); const url_1 = require("url"); const logger_1 = require("../lib/logger"); class SSLAnalyzerService { async analyzeSSL(url) { const parsedUrl = new url_1.URL(url); const host = parsedUrl.hostname; const port = parsedUrl.port ? parseInt(parsedUrl.port) : 443; logger_1.logger.info(`Starting SSL analysis for: ${host}:${port}`); const result = { host, port, warnings: [], errors: [], securityScore: 0, recommendations: [], }; if (parsedUrl.protocol !== 'https:') { result.errors.push('URL is not HTTPS'); result.recommendations.push('Use HTTPS to secure communications'); return result; } try { const certificate = await this.getCertificateInfo(host, port); result.certificate = certificate; this.analyzeCertificateSecurity(result); logger_1.logger.info(`SSL analysis completed for: ${host}:${port}`, { valid: certificate.valid, daysToExpiry: certificate.daysToExpiry, securityScore: result.securityScore }); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown SSL error'; result.errors.push(`SSL analysis failed: ${errorMessage}`); logger_1.logger.error(`SSL analysis failed for ${host}:${port}:`, error); } return result; } async getCertificateInfo(host, port) { return new Promise((resolve, reject) => { const options = { host, port, servername: host, rejectUnauthorized: false, timeout: 10000, }; const socket = tls_1.default.connect(options, () => { try { const cert = socket.getPeerCertificate(true); const cipher = socket.getCipher(); const protocol = socket.getProtocol(); if (!cert || Object.keys(cert).length === 0) { socket.end(); reject(new Error('No certificate found')); return; } const now = new Date(); const validFrom = new Date(cert.valid_from); const validTo = new Date(cert.valid_to); const daysToExpiry = Math.floor((validTo.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)); const certificateInfo = { valid: socket.authorized, subject: this.parseCertificateField(cert.subject), issuer: this.parseCertificateField(cert.issuer), validFrom: validFrom.toISOString(), validTo: validTo.toISOString(), daysToExpiry, serialNumber: cert.serialNumber, fingerprint: cert.fingerprint, signatureAlgorithm: cert.signatureAlgorithm, keySize: cert.bits, protocol, cipher: cipher ? { name: cipher.name, version: cipher.version, } : undefined, }; socket.end(); resolve(certificateInfo); } catch (error) { socket.end(); reject(error); } }); socket.on('error', (error) => { reject(error); }); socket.on('timeout', () => { socket.destroy(); reject(new Error('SSL connection timeout')); }); socket.setTimeout(10000); }); } parseCertificateField(field) { if (!field) return {}; return { commonName: field.CN, organization: field.O, organizationalUnit: field.OU, locality: field.L, state: field.ST, country: field.C, }; } analyzeCertificateSecurity(result) { const cert = result.certificate; if (!cert) return; let score = 100; if (!cert.valid) { result.warnings.push('Certificate is not valid or trusted'); result.recommendations.push('Install a valid SSL certificate from a trusted CA'); score -= 30; } if (cert.daysToExpiry < 0) { result.errors.push('Certificate has expired'); result.recommendations.push('Renew the SSL certificate immediately'); score -= 40; } else if (cert.daysToExpiry < 30) { result.warnings.push(`Certificate expires in ${cert.daysToExpiry} days`); result.recommendations.push('Renew the SSL certificate soon'); score -= 10; } else if (cert.daysToExpiry < 90) { result.warnings.push(`Certificate expires in ${cert.daysToExpiry} days`); score -= 5; } if (cert.keySize && cert.keySize < 2048) { result.warnings.push(`Weak key size: ${cert.keySize} bits`); result.recommendations.push('Use at least 2048-bit RSA keys or 256-bit ECC keys'); score -= 20; } if (cert.signatureAlgorithm) { if (cert.signatureAlgorithm.toLowerCase().includes('sha1')) { result.warnings.push('Using deprecated SHA-1 signature algorithm'); result.recommendations.push('Upgrade to SHA-256 or better signature algorithm'); score -= 15; } else if (cert.signatureAlgorithm.toLowerCase().includes('md5')) { result.errors.push('Using insecure MD5 signature algorithm'); result.recommendations.push('Immediately upgrade to SHA-256 or better'); score -= 30; } } if (cert.protocol) { if (cert.protocol.includes('TLSv1.0') || cert.protocol.includes('TLSv1.1')) { result.warnings.push(`Using deprecated protocol: ${cert.protocol}`); result.recommendations.push('Upgrade to TLS 1.2 or TLS 1.3'); score -= 15; } else if (cert.protocol.includes('SSLv')) { result.errors.push(`Using insecure protocol: ${cert.protocol}`); result.recommendations.push('Immediately upgrade to TLS 1.2 or TLS 1.3'); score -= 35; } } if (cert.cipher?.name) { const cipherName = cert.cipher.name.toLowerCase(); if (cipherName.includes('rc4') || cipherName.includes('des')) { result.errors.push(`Weak cipher suite: ${cert.cipher.name}`); result.recommendations.push('Use strong cipher suites like AES-GCM'); score -= 25; } else if (cipherName.includes('cbc')) { result.warnings.push(`CBC cipher mode detected: ${cert.cipher.name}`); result.recommendations.push('Prefer GCM or ChaCha20-Poly1305 cipher modes'); score -= 5; } } if (!cert.subject.commonName) { result.warnings.push('Certificate missing Common Name'); score -= 5; } result.securityScore = Math.max(0, score); if (result.securityScore >= 90) { result.recommendations.push('SSL configuration looks excellent!'); } else if (result.securityScore >= 70) { result.recommendations.push('SSL configuration is good with minor improvements possible'); } else if (result.securityScore >= 50) { result.recommendations.push('SSL configuration needs improvement for better security'); } else { result.recommendations.push('SSL configuration requires immediate attention'); } } async quickSSLCheck(url) { try { const analysis = await this.analyzeSSL(url); return { valid: analysis.certificate?.valid ?? false, daysToExpiry: analysis.certificate?.daysToExpiry, warnings: analysis.warnings, }; } catch (error) { logger_1.logger.warn(`Quick SSL check failed for ${url}:`, error); return { valid: false, warnings: ['SSL check failed'], }; } } } exports.SSLAnalyzerService = SSLAnalyzerService; //# sourceMappingURL=ssl-analyzer.service.js.map