crtsh_search
Search Certificate Transparency logs to discover subdomains and certificate details for any domain.
Instructions
Search Certificate Transparency logs via crt.sh. Returns unique subdomains and certificate details (issuer, validity, SANs).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| domain | Yes | Domain to search CT logs for | |
| exclude_expired | No | Exclude expired certificates (default: false) |
Implementation Reference
- src/crtsh/index.ts:27-87 (handler)The main handler function that queries crt.sh Certificate Transparency logs, deduplicates subdomains, and returns certificates with caching and rate limiting.
export async function crtshSearch(domain: string, excludeExpired = false): Promise<CrtshResult> { const cacheKey = `${domain}:${excludeExpired}`; const cached = cache.get(cacheKey); if (cached) return cached; await limiter.acquire(); const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 30000); try { const url = `https://crt.sh/?q=%25.${encodeURIComponent(domain)}&output=json`; const res = await fetch(url, { signal: controller.signal }); if (!res.ok) throw new Error(`crt.sh returned ${res.status}`); const data: any[] = await res.json(); const now = Date.now(); // Deduplicate subdomains const subdomainSet = new Set<string>(); const certificates: CrtshCert[] = []; for (const entry of data) { const nameValue: string = entry.name_value ?? ""; const notAfter = entry.not_after ? new Date(entry.not_after).getTime() : Infinity; if (excludeExpired && notAfter < now) continue; // name_value can contain multiple domains separated by \n const names = nameValue.split("\n").map((n: string) => n.trim().toLowerCase()).filter(Boolean); for (const name of names) { if (!name.startsWith("*")) subdomainSet.add(name); else subdomainSet.add(name); // Keep wildcards too } certificates.push({ issuer: entry.issuer_name ?? "", commonName: entry.common_name ?? "", nameValue, notBefore: entry.not_before ?? "", notAfter: entry.not_after ?? "", id: entry.id ?? 0, }); } // Sort subdomains and limit certificates shown const uniqueSubdomains = [...subdomainSet].sort(); const result: CrtshResult = { domain, totalCerts: data.length, uniqueSubdomains, certificates: certificates.slice(0, 50), // Limit to avoid huge responses }; cache.set(cacheKey, result); return result; } finally { clearTimeout(timeout); } } - src/crtsh/index.ts:9-23 (schema)Type definitions for CrtshCert (certificate data) and CrtshResult (output structure with domain, totalCerts, uniqueSubdomains, certificates).
interface CrtshCert { issuer: string; commonName: string; nameValue: string; notBefore: string; notAfter: string; id: number; } interface CrtshResult { domain: string; totalCerts: number; uniqueSubdomains: string[]; certificates: CrtshCert[]; } - src/protocol/tools.ts:112-121 (registration)Tool registration with name 'crtsh_search', description, Zod schema (domain required, exclude_expired optional), and execute handler that calls crtshSearch.
const crtshSearchTool: ToolDef = { name: "crtsh_search", description: "Search Certificate Transparency logs via crt.sh. Returns unique subdomains and certificate details (issuer, validity, SANs).", schema: { domain: z.string().describe("Domain to search CT logs for"), exclude_expired: z.boolean().optional().describe("Exclude expired certificates (default: false)"), }, execute: async (args) => json(await crtshSearch(args.domain as string, args.exclude_expired as boolean | undefined)), }; - src/protocol/tools.ts:493-493 (registration)Tool added to the master tools array for registration in the protocol layer.
crtshSearchTool, - src/meta/recon.ts:47-50 (helper)Usage of crtshSearch as a subdomain source inside the domainRecon meta-tool, called alongside other OSINT sources.
crtshSearch(domain), hackertargetHostsearch(domain), dnsEmailSecurity(domain), ]);