UNPKG

@burtthecoder/mcp-virustotal

Version:
172 lines (171 loc) • 9.02 kB
// src/formatters/url.ts import { formatDateTime, formatDetectionResults } from './utils.js'; import { logToFile } from '../utils/logging.js'; export function formatRelationshipData(relType, item) { const attrs = item.attributes || {}; switch (relType) { case 'communicating_files': case 'downloaded_files': return ` • ${attrs.meaningful_name || item.id} Type: ${attrs.type_description || attrs.type || 'Unknown'} First Seen: ${attrs.first_submission_date ? formatDateTime(attrs.first_submission_date) : 'Unknown'}`; case 'contacted_domains': return ` • ${attrs.id || item.id} Last DNS Resolution: ${attrs.last_dns_records_date ? formatDateTime(attrs.last_dns_records_date) : 'Unknown'} Categories: ${Object.entries(attrs.categories || {}).map(([k, v]) => `${k}: ${v}`).join(', ') || 'None'}`; case 'contacted_ips': return ` • ${attrs.ip_address || item.id} Country: ${attrs.country || 'Unknown'} AS Owner: ${attrs.as_owner || 'Unknown'} Last Analysis Stats: ${attrs.last_analysis_stats ? `šŸ”“ ${attrs.last_analysis_stats.malicious} malicious, āœ… ${attrs.last_analysis_stats.harmless} harmless` : 'Unknown'}`; case 'redirects_to': case 'redirecting_urls': return ` • ${attrs.url || item.id} Last Analysis: ${attrs.last_analysis_date ? formatDateTime(attrs.last_analysis_date) : 'Unknown'} Reputation: ${attrs.reputation ?? 'Unknown'}`; case 'related_threat_actors': return ` • ${attrs.name || item.id} Description: ${attrs.description || 'No description available'}`; default: return ` • ${item.id}`; } } export function formatUrlScanResults(data) { try { const attributes = data?.attributes || {}; const stats = attributes?.last_analysis_stats || {}; const votes = attributes?.total_votes || { harmless: 0, malicious: 0 }; const tags = attributes?.tags || []; const redirectionChain = attributes?.redirection_chain || []; const outgoingLinks = attributes?.outgoing_links || []; let outputArray = [ `šŸ” URL Analysis Results`, ``, `🌐 URL Information:`, `• URL: ${attributes?.url || data?.url || data?.id || 'Unknown URL'}`, attributes?.last_final_url && attributes.last_final_url !== attributes.url ? `• Final URL: ${attributes.last_final_url}` : null, attributes?.title ? `• Page Title: ${attributes.title}` : null, `• First Seen: ${attributes.first_submission_date ? formatDateTime(attributes.first_submission_date) : 'N/A'}`, `• Last Analyzed: ${attributes.last_analysis_date ? formatDateTime(attributes.last_analysis_date) : 'N/A'}`, `• Times Submitted: ${attributes?.times_submitted || 0}`, ``, `šŸ“Š Analysis Statistics:`, formatDetectionResults(stats), ].filter(Boolean); // Add reputation and votes const reputation = attributes?.reputation ?? 'N/A'; outputArray.push(``, `šŸ‘„ Community Feedback:`, `• Reputation Score: ${reputation}`, `• Harmless Votes: ${votes.harmless}`, `• Malicious Votes: ${votes.malicious}`); // Add HTTP response details if (attributes?.last_http_response_code) { outputArray.push(``, `🌐 HTTP Response:`, `• Status Code: ${attributes.last_http_response_code}`, `• Content Length: ${formatSize(attributes.last_http_response_content_length || 0)}`, attributes.last_http_response_content_sha256 ? `• Content SHA-256: ${attributes.last_http_response_content_sha256}` : null); } // Add categories if available if (attributes?.categories && Object.keys(attributes.categories).length > 0) { outputArray.push(``, `šŸ·ļø Categories:`, ...Object.entries(attributes.categories).map(([service, category]) => `• ${service}: ${category}`)); } // Add redirection chain if available if (redirectionChain.length > 0) { outputArray.push(``, `ā†Ŗļø Redirection Chain:`, ...redirectionChain.map((url, index) => `${index + 1}. ${url}`)); } // Add outgoing links if available if (outgoingLinks.length > 0) { outputArray.push(``, `šŸ”— Outgoing Links:`, ...outgoingLinks.slice(0, 5).map((url) => `• ${url}`), outgoingLinks.length > 5 ? `... and ${outgoingLinks.length - 5} more` : null); } // Add trackers if available if (attributes?.trackers && Object.keys(attributes.trackers).length > 0) { outputArray.push(``, `šŸ“” Trackers:`); for (const [tracker, instances] of Object.entries(attributes.trackers)) { if (Array.isArray(instances)) { outputArray.push(`${tracker}:`, ...instances.map((instance) => `• ID: ${instance.id}${instance.url ? `\n URL: ${instance.url}` : ''}`)); } } } // Add targeted brand if available if (attributes?.targeted_brand && Object.keys(attributes.targeted_brand).length > 0) { outputArray.push(``, `šŸŽÆ Targeted Brands:`, ...Object.entries(attributes.targeted_brand).map(([source, brand]) => `• ${source}: ${brand}`)); } // Add meta information if available if (attributes?.html_meta && Object.keys(attributes.html_meta).length > 0) { const relevantMeta = ['description', 'keywords', 'author']; const metaEntries = Object.entries(attributes.html_meta) .filter(([key]) => relevantMeta.includes(key)); if (metaEntries.length > 0) { outputArray.push(``, `šŸ“ Meta Information:`); for (const [key, values] of metaEntries) { if (Array.isArray(values) && values.length > 0) { outputArray.push(`• ${key}: ${values[0]}`); } } } } // Add favicon information if available if (attributes?.favicon) { outputArray.push(``, `šŸ–¼ļø Favicon:`, `• Hash: ${attributes.favicon.dhash}`, `• MD5: ${attributes.favicon.raw_md5}`); } // Add tags if available if (tags.length > 0) { outputArray.push(``, `šŸ·ļø Tags:`, ...tags.map((tag) => `• ${tag}`)); } // Add HTTP response headers if available if (attributes?.last_http_response_headers && Object.keys(attributes.last_http_response_headers).length > 0) { const importantHeaders = ['server', 'content-type', 'x-powered-by', 'x-frame-options', 'x-xss-protection']; const relevantHeaders = Object.entries(attributes.last_http_response_headers) .filter(([key]) => importantHeaders.includes(key.toLowerCase())); if (relevantHeaders.length > 0) { outputArray.push(``, `šŸ“‹ Important HTTP Headers:`); for (const [key, value] of relevantHeaders) { outputArray.push(`• ${key}: ${value}`); } } } // Add HTTP response cookies if available if (attributes?.last_http_response_cookies && Object.keys(attributes.last_http_response_cookies).length > 0) { outputArray.push(``, `šŸŖ Cookies:`, ...Object.entries(attributes.last_http_response_cookies) .map(([name, value]) => `• ${name}: ${value}`)); } // Format relationships if available if (data.relationships) { outputArray.push('\nšŸ”— Relationships:'); for (const [relType, relData] of Object.entries(data.relationships)) { const count = relData.meta?.count || (Array.isArray(relData.data) ? relData.data.length : 1); outputArray.push(`\n${relType} (${count} items):`); if (Array.isArray(relData.data)) { relData.data.forEach(item => { outputArray.push(formatRelationshipData(relType, item)); }); } else if (relData.data) { outputArray.push(formatRelationshipData(relType, relData.data)); } } } return { type: "text", text: outputArray.filter(Boolean).join('\n') }; } catch (error) { logToFile(`Error formatting URL scan results: ${error}`); return { type: "text", text: "Error formatting URL scan results" }; } } function formatSize(bytes) { const units = ['B', 'KB', 'MB', 'GB']; let size = bytes; let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } return `${size.toFixed(2)} ${units[unitIndex]}`; }