domainlooker
Version:
šµļø Mission-critical domain intelligence gathering tool with spy-themed interface. Comprehensive WHOIS, DNS, SSL, network analysis, subdomain discovery, and API-ready JSON exports.
476 lines ⢠22.8 kB
JavaScript
import chalk from 'chalk';
import Table from 'cli-table3';
import { WhoisService } from './services/whois.js';
import { DNSService } from './services/dns.js';
import { SSLService } from './services/ssl.js';
import { NetworkService } from './services/network.js';
import { SubdomainService } from './services/subdomain.js';
import { CSVExportService } from './services/csv-export.js';
import { JsonExportService } from './services/json-export.js';
import { createSpinner, missionComplete, criticalAlert } from './ui/effects.js';
export class DomainInspector {
constructor(options = {}) {
this.options = options;
this.whoisService = new WhoisService();
this.dnsService = new DNSService();
this.sslService = new SSLService();
this.networkService = new NetworkService();
this.subdomainService = new SubdomainService();
// Initialize CSV exporter if export option is provided
if (this.options.exportCsv) {
this.csvExporter = new CSVExportService();
}
// Initialize JSON exporter if export option is provided
if (this.options.exportJson) {
this.jsonExporter = new JsonExportService(this.options);
}
}
async investigateMultiple(domains, parallelLimit = 3) {
console.log(chalk.magenta.bold('\n' + 'ā'.repeat(80)));
console.log(chalk.magenta.bold(`šÆ MULTIPLE TARGETS ACQUIRED: ${domains.length} DOMAINS`));
console.log(chalk.magenta.bold(`š” Processing ${parallelLimit} domains in parallel`));
console.log(chalk.magenta.bold('ā'.repeat(80)));
// Process domains in batches
for (let i = 0; i < domains.length; i += parallelLimit) {
const batch = domains.slice(i, i + parallelLimit);
const batchNum = Math.floor(i / parallelLimit) + 1;
const totalBatches = Math.ceil(domains.length / parallelLimit);
console.log(chalk.cyan.bold(`\nš BATCH ${batchNum}/${totalBatches}: ${batch.join(' ⢠')}`));
// Process current batch in parallel
const batchPromises = batch.map(domain => this.investigateSingle(domain));
await Promise.allSettled(batchPromises);
// Add separator between batches (except for last batch)
if (i + parallelLimit < domains.length) {
console.log('\n' + chalk.magenta('ā'.repeat(80)));
console.log(chalk.yellow.bold('ā³ PREPARING NEXT BATCH...'));
console.log(chalk.magenta('ā'.repeat(80)));
await new Promise(resolve => setTimeout(resolve, 1500)); // Brief pause between batches
}
}
// Final completion summary
console.log('\n' + chalk.green.bold('ā'.repeat(80)));
console.log(chalk.green.bold(`š MISSION COMPLETE: ALL ${domains.length} DOMAINS ANALYZED`));
console.log(chalk.green.bold('ā'.repeat(80)));
// Export to CSV if requested
if (this.csvExporter && this.options.exportCsv) {
await this.csvExporter.exportToFile(this.options.exportCsv);
}
// Export to JSON if requested
if (this.jsonExporter && this.options.exportJson) {
await this.jsonExporter.exportToFile(this.options.exportJson);
}
}
async investigateSingle(domain) {
try {
// Clear separation with domain header
console.log('\n' + chalk.yellow('='.repeat(80)));
console.log(chalk.yellow.bold(`šÆ ANALYZING TARGET: ${domain.toUpperCase()}`));
console.log(chalk.yellow('='.repeat(80)));
console.log(chalk.cyan('š” Initiating intelligence gathering operations...\n'));
const domainInfo = { domain };
// Execute intelligence gathering (same parallel logic as original)
const whoisSpinner = createSpinner(`WHOIS: ${domain}`);
const dnsSpinner = createSpinner(`DNS: ${domain}`);
const sslSpinner = createSpinner(`SSL: ${domain}`);
const networkSpinner = this.options.quick ? null : createSpinner(`Network: ${domain}`);
const subdomainSpinner = this.options.subdomains ? createSpinner(`Subdomains: ${domain}`) : null;
whoisSpinner.start();
dnsSpinner.start();
sslSpinner.start();
if (networkSpinner)
networkSpinner.start();
if (subdomainSpinner)
subdomainSpinner.start();
const intelligencePromises = [
this.gatherWhoisIntelligence(domain, whoisSpinner),
this.gatherDnsIntelligence(domain, dnsSpinner),
this.gatherSslIntelligence(domain, sslSpinner),
...(this.options.quick ? [] : [this.gatherNetworkIntelligence(domain, networkSpinner)]),
...(this.options.subdomains ? [this.gatherSubdomainIntelligence(domain, subdomainSpinner)] : [])
];
const results = await Promise.allSettled(intelligencePromises);
// Process results more carefully for multiple domain scenario
let resultIndex = 0;
domainInfo.whois = results[resultIndex].status === 'fulfilled' ? results[resultIndex].value : null;
resultIndex++;
domainInfo.dns = results[resultIndex].status === 'fulfilled' ? results[resultIndex].value : null;
resultIndex++;
domainInfo.ssl = results[resultIndex].status === 'fulfilled' ? results[resultIndex].value : null;
resultIndex++;
if (!this.options.quick) {
domainInfo.network = results[resultIndex].status === 'fulfilled' ? results[resultIndex].value : null;
resultIndex++;
}
if (this.options.subdomains) {
domainInfo.subdomains = results[resultIndex].status === 'fulfilled' ? results[resultIndex].value : null;
}
// Use full detailed report for each domain
await this.generateIntelligenceReport(domainInfo);
// Add to CSV export if enabled
if (this.csvExporter) {
this.csvExporter.addDomain(domainInfo);
}
// Add to JSON export if enabled
if (this.jsonExporter) {
this.jsonExporter.addDomain(domainInfo);
}
}
catch (error) {
console.log(chalk.red(`ā Failed to analyze ${domain}: ${error}`));
}
}
async investigate(domain) {
console.log(chalk.yellow(`šÆ TARGET ACQUIRED: ${domain.toUpperCase()}`));
console.log(chalk.cyan('š” Initiating intelligence gathering operations...\n'));
const domainInfo = { domain };
try {
// Initialize all spinners
const whoisSpinner = createSpinner('Accessing WHOIS intelligence database...');
const dnsSpinner = createSpinner('Intercepting DNS communications...');
const sslSpinner = createSpinner('Analyzing encryption protocols...');
const networkSpinner = this.options.quick ? null : createSpinner('Conducting network reconnaissance...');
const subdomainSpinner = this.options.subdomains ? createSpinner('Discovering subdomains...') : null;
// Start all spinners
whoisSpinner.start();
dnsSpinner.start();
sslSpinner.start();
if (networkSpinner)
networkSpinner.start();
if (subdomainSpinner)
subdomainSpinner.start();
// Execute all intelligence gathering operations in parallel
const intelligencePromises = [
this.gatherWhoisIntelligence(domain, whoisSpinner),
this.gatherDnsIntelligence(domain, dnsSpinner),
this.gatherSslIntelligence(domain, sslSpinner),
...(this.options.quick ? [] : [this.gatherNetworkIntelligence(domain, networkSpinner)]),
...(this.options.subdomains ? [this.gatherSubdomainIntelligence(domain, subdomainSpinner)] : [])
];
const results = await Promise.allSettled(intelligencePromises);
// Process results more carefully for single domain scenario
let resultIndex = 0;
domainInfo.whois = results[resultIndex].status === 'fulfilled' ? results[resultIndex].value : null;
resultIndex++;
domainInfo.dns = results[resultIndex].status === 'fulfilled' ? results[resultIndex].value : null;
resultIndex++;
domainInfo.ssl = results[resultIndex].status === 'fulfilled' ? results[resultIndex].value : null;
resultIndex++;
if (!this.options.quick) {
domainInfo.network = results[resultIndex].status === 'fulfilled' ? results[resultIndex].value : null;
resultIndex++;
}
if (this.options.subdomains) {
domainInfo.subdomains = results[resultIndex].status === 'fulfilled' ? results[resultIndex].value : null;
}
await this.generateIntelligenceReport(domainInfo);
// Add to CSV export if enabled
if (this.csvExporter) {
this.csvExporter.addDomain(domainInfo);
await this.csvExporter.exportToFile(this.options.exportCsv);
}
// Add to JSON export if enabled
if (this.jsonExporter) {
this.jsonExporter.addDomain(domainInfo);
await this.jsonExporter.exportToFile(this.options.exportJson);
}
}
catch (error) {
criticalAlert(`Mission failed: ${error}`);
}
}
async generateIntelligenceReport(info) {
console.log('\n' + chalk.yellow('='.repeat(60)));
console.log(chalk.yellow.bold(`š INTELLIGENCE REPORT: ${info.domain.toUpperCase()}`));
console.log(chalk.yellow('='.repeat(60)) + '\n');
// WHOIS Intelligence
if (info.whois) {
console.log(chalk.cyan.bold('š REGISTRATION INTELLIGENCE'));
const whoisTable = new Table({
style: { head: ['cyan'] },
colWidths: [20, 40]
});
if (info.whois.registrar)
whoisTable.push(['Registrar', info.whois.registrar]);
if (info.whois.registrationDate)
whoisTable.push(['Registered', info.whois.registrationDate]);
if (info.whois.expirationDate)
whoisTable.push(['Expires', info.whois.expirationDate]);
if (info.whois.registrantCountry)
whoisTable.push(['Country', info.whois.registrantCountry]);
if (info.whois.status)
whoisTable.push(['Status', info.whois.status.join(', ')]);
console.log(whoisTable.toString());
console.log();
}
// DNS Intelligence
if (info.dns) {
console.log(chalk.green.bold('š DNS INTELLIGENCE'));
const dnsTable = new Table({
style: { head: ['green'] },
colWidths: [15, 45]
});
if (info.dns.a && info.dns.a.length > 0) {
dnsTable.push(['A Records', info.dns.a.join('\n')]);
}
if (info.dns.aaaa && info.dns.aaaa.length > 0) {
dnsTable.push(['AAAA Records', info.dns.aaaa.join('\n')]);
}
if (info.dns.mx && info.dns.mx.length > 0) {
const mxRecords = info.dns.mx.map(mx => `${mx.priority} ${mx.exchange}`).join('\n');
dnsTable.push(['MX Records', mxRecords]);
}
if (info.dns.ns && info.dns.ns.length > 0) {
dnsTable.push(['NS Records', info.dns.ns.join('\n')]);
}
if (info.dns.txt && info.dns.txt.length > 0) {
dnsTable.push(['TXT Records', info.dns.txt.slice(0, 3).join('\n')]);
}
console.log(dnsTable.toString());
console.log();
}
// SSL Certificate Intelligence
if (info.ssl) {
console.log(chalk.magenta.bold('š ENCRYPTION INTELLIGENCE'));
const sslTable = new Table({
style: { head: ['magenta'] },
colWidths: [20, 40]
});
if (info.ssl.subject)
sslTable.push(['Subject', info.ssl.subject]);
if (info.ssl.issuer)
sslTable.push(['Issuer', info.ssl.issuer]);
if (info.ssl.validFrom)
sslTable.push(['Valid From', info.ssl.validFrom]);
if (info.ssl.validTo)
sslTable.push(['Valid To', info.ssl.validTo]);
if (info.ssl.daysUntilExpiry !== undefined) {
const expiryColor = info.ssl.daysUntilExpiry < 30 ? 'red' :
info.ssl.daysUntilExpiry < 90 ? 'yellow' : 'green';
sslTable.push(['Days Until Expiry', chalk[expiryColor](`${info.ssl.daysUntilExpiry} days`)]);
}
if (info.ssl.fingerprint)
sslTable.push(['Fingerprint', info.ssl.fingerprint]);
if (info.ssl.signatureAlgorithm)
sslTable.push(['Signature Algorithm', info.ssl.signatureAlgorithm]);
console.log(sslTable.toString());
console.log();
}
// Network Intelligence
if (info.network && info.network.openPorts && info.network.openPorts.length > 0) {
console.log(chalk.blue.bold('š NETWORK INTELLIGENCE'));
const networkTable = new Table({
style: { head: ['blue'] },
head: ['Port', 'Protocol', 'Service', 'Status'],
colWidths: [8, 12, 15, 10]
});
info.network.services?.forEach(service => {
networkTable.push([
service.port.toString(),
service.protocol,
service.service,
chalk.red('OPEN')
]);
});
console.log(networkTable.toString());
console.log();
}
// Subdomain Intelligence
if (info.subdomains && info.subdomains.totalFound > 0) {
console.log(chalk.magenta.bold('š SUBDOMAIN INTELLIGENCE'));
// Summary table
const subdomainSummary = new Table({
style: { head: ['magenta'] },
colWidths: [25, 35]
});
subdomainSummary.push(['Total Subdomains Found', info.subdomains.totalFound.toString()], ['Certificate Transparency', info.subdomains.sources.certificateTransparency.length.toString()], ['Common Names Found', info.subdomains.sources.commonNames.length.toString()], ['DNS Enumeration', info.subdomains.sources.dnsEnumeration.length.toString()]);
console.log(subdomainSummary.toString());
// Display found subdomains (limit to first 20 for readability)
if (info.subdomains.subdomains.length > 0) {
console.log(chalk.magenta.bold('\nš DISCOVERED SUBDOMAINS'));
const subdomainList = new Table({
style: { head: ['magenta'] },
head: ['Subdomain', 'Source'],
colWidths: [40, 20]
});
const displayLimit = Math.min(20, info.subdomains.subdomains.length);
for (let i = 0; i < displayLimit; i++) {
const subdomain = info.subdomains.subdomains[i];
let source = 'Unknown';
if (info.subdomains.sources.certificateTransparency.includes(subdomain)) {
source = 'Cert Transparency';
}
else if (info.subdomains.sources.commonNames.includes(subdomain)) {
source = 'Common Name';
}
else if (info.subdomains.sources.dnsEnumeration.includes(subdomain)) {
source = 'DNS Enum';
}
subdomainList.push([subdomain, source]);
}
console.log(subdomainList.toString());
if (info.subdomains.subdomains.length > 20) {
console.log(chalk.yellow(`... and ${info.subdomains.subdomains.length - 20} more subdomains`));
}
}
console.log();
}
// Threat Assessment
this.displayThreatAssessment(info);
missionComplete(`Intelligence gathering complete for ${info.domain}`);
}
async generateCompactReport(info) {
console.log(chalk.cyan(`\nš INTELLIGENCE SUMMARY: ${info.domain.toUpperCase()}`));
const summary = new Table({
style: { head: ['cyan'] },
colWidths: [25, 35]
});
// Quick status overview
const whoisStatus = info.whois ? 'ā
Available' : 'ā Unavailable';
const dnsStatus = info.dns && (info.dns.a || info.dns.aaaa) ? 'ā
Resolved' : 'ā Failed';
const sslStatus = info.ssl ? `ā
Valid (${info.ssl.daysUntilExpiry}d)` : 'ā No Certificate';
const networkStatus = info.network?.openPorts?.length ?
`ā
${info.network.openPorts.length} ports open` : 'ā No ports detected';
summary.push(['WHOIS Intelligence', whoisStatus], ['DNS Resolution', dnsStatus], ['SSL Certificate', sslStatus], ['Network Services', networkStatus]);
// Key details
if (info.whois?.registrar) {
summary.push(['Registrar', info.whois.registrar]);
}
if (info.whois?.expirationDate) {
summary.push(['Expires', info.whois.expirationDate]);
}
if (info.dns?.a && info.dns.a.length > 0) {
summary.push(['IPv4 Address', info.dns.a[0]]);
}
if (info.ssl?.issuer) {
summary.push(['SSL Issuer', info.ssl.issuer.split(',')[0]]);
}
console.log(summary.toString());
// Quick threat assessment
const threats = [];
if (info.ssl?.daysUntilExpiry !== undefined && info.ssl.daysUntilExpiry < 30) {
threats.push('ā ļø SSL expires soon');
}
if (!info.ssl) {
threats.push('ā ļø No SSL certificate');
}
if (info.whois?.registrationDate) {
const regDate = new Date(info.whois.registrationDate);
const daysSinceReg = (Date.now() - regDate.getTime()) / (1000 * 60 * 60 * 24);
if (daysSinceReg < 30) {
threats.push('ā ļø Recently registered');
}
}
if (threats.length > 0) {
console.log(chalk.red('\nšØ ALERTS: ' + threats.join(' | ')));
}
else {
console.log(chalk.green('\nā
No immediate threats detected'));
}
}
async gatherWhoisIntelligence(domain, spinner) {
try {
const result = await this.whoisService.lookup(domain);
spinner.succeed(chalk.green('WHOIS intelligence gathered'));
return result;
}
catch (error) {
spinner.fail(chalk.red('WHOIS intelligence unavailable'));
if (this.options.verbose) {
console.log(chalk.red(`Error: ${error}`));
}
return null;
}
}
async gatherDnsIntelligence(domain, spinner) {
try {
const result = await this.dnsService.lookup(domain);
spinner.succeed(chalk.green('DNS intelligence captured'));
return result;
}
catch (error) {
spinner.fail(chalk.red('DNS intelligence compromised'));
if (this.options.verbose) {
console.log(chalk.red(`Error: ${error}`));
}
return null;
}
}
async gatherSslIntelligence(domain, spinner) {
try {
const result = await this.sslService.getCertificate(domain);
spinner.succeed(chalk.green('Encryption analysis complete'));
return result;
}
catch (error) {
spinner.fail(chalk.red('Encryption analysis failed'));
if (this.options.verbose) {
console.log(chalk.red(`Error: ${error}`));
}
return null;
}
}
async gatherNetworkIntelligence(domain, spinner) {
try {
const result = await this.networkService.getNetworkInfo(domain);
spinner.succeed(chalk.green('Network reconnaissance complete'));
return result;
}
catch (error) {
spinner.fail(chalk.red('Network reconnaissance failed'));
if (this.options.verbose) {
console.log(chalk.red(`Error: ${error}`));
}
return null;
}
}
async gatherSubdomainIntelligence(domain, spinner) {
try {
const result = await this.subdomainService.discoverSubdomains(domain);
spinner.succeed(chalk.green(`Subdomain discovery complete (${result.totalFound} found)`));
return result;
}
catch (error) {
spinner.fail(chalk.red('Subdomain discovery failed'));
if (this.options.verbose) {
console.log(chalk.red(`Error: ${error}`));
}
return null;
}
}
displayThreatAssessment(info) {
console.log(chalk.red.bold('ā ļø THREAT ASSESSMENT'));
const threats = [];
// Check SSL expiry
if (info.ssl?.daysUntilExpiry !== undefined && info.ssl.daysUntilExpiry < 30) {
threats.push('SSL certificate expires soon');
}
// Check if no SSL
if (!info.ssl) {
threats.push('No SSL certificate detected');
}
// Check domain age (if registration is recent)
if (info.whois?.registrationDate) {
const regDate = new Date(info.whois.registrationDate);
const now = new Date();
const daysSinceReg = (now.getTime() - regDate.getTime()) / (1000 * 60 * 60 * 24);
if (daysSinceReg < 30) {
threats.push('Domain registered very recently');
}
}
const threatTable = new Table({
style: { head: ['red'] },
colWidths: [60]
});
if (threats.length === 0) {
threatTable.push([chalk.green('ā
No immediate threats detected')]);
}
else {
threats.forEach(threat => {
threatTable.push([chalk.red(`ā ļø ${threat}`)]);
});
}
console.log(threatTable.toString());
console.log();
}
}
//# sourceMappingURL=domain-inspector.js.map