dns_email_security
Analyze email security posture by checking SPF, DMARC, and DKIM records with risk assessment and recommendations for domains.
Instructions
Analyze email security posture: SPF, DMARC, DKIM records with risk assessment and recommendations. Checks common DKIM selectors (google, selector1, selector2, k1, etc.).
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| domain | Yes | Domain to analyze | |
| dkim_selectors | No | Custom DKIM selectors to check (default: common selectors) |
Implementation Reference
- src/dns/index.ts:137-242 (handler)The handler function for dns_email_security that analyzes SPF, DMARC, and DKIM records to assess email security posture.
export async function dnsEmailSecurity(domain: string, dkimSelectors?: string[]): Promise<EmailSecurityResult> { const selectors = dkimSelectors ?? DEFAULT_DKIM_SELECTORS; const recommendations: string[] = []; // SPF let spf: EmailSecurityResult["spf"] = { found: false, risk: "high" }; try { const txts = await dns.resolveTxt(domain); const spfRecord = txts.map((t) => t.join("")).find((t) => t.startsWith("v=spf1")); if (spfRecord) { const includes = [...spfRecord.matchAll(/include:(\S+)/g)].map((m) => m[1]); let policy = "neutral"; if (spfRecord.includes("-all")) policy = "hard_fail"; else if (spfRecord.includes("~all")) policy = "soft_fail"; else if (spfRecord.includes("+all")) policy = "pass_all"; else if (spfRecord.includes("?all")) policy = "neutral"; let risk = "low"; if (policy === "pass_all") risk = "critical"; else if (policy === "soft_fail") risk = "medium"; else if (policy === "neutral") risk = "medium"; if (policy === "soft_fail") recommendations.push("Upgrade SPF from ~all (soft fail) to -all (hard fail)"); if (policy === "pass_all") recommendations.push("SPF +all allows any IP to send email — change to -all immediately"); spf = { found: true, record: spfRecord, policy, includes, risk }; } else { recommendations.push("No SPF record found — add v=spf1 with authorized senders"); spf = { found: false, risk: "critical" }; } } catch { spf = { found: false, risk: "high" }; } // DMARC let dmarc: EmailSecurityResult["dmarc"] = { found: false, risk: "critical" }; try { const txts = await dns.resolveTxt(`_dmarc.${domain}`); const dmarcRecord = txts.map((t) => t.join("")).find((t) => t.startsWith("v=DMARC1")); if (dmarcRecord) { const pMatch = dmarcRecord.match(/;\s*p=(\w+)/); const spMatch = dmarcRecord.match(/;\s*sp=(\w+)/); const ruaMatch = dmarcRecord.match(/;\s*rua=mailto:([^\s;]+)/); const policy = pMatch?.[1] ?? "none"; const subdomainPolicy = spMatch?.[1]; const rua = ruaMatch?.[1]; let risk = "low"; if (policy === "none") { risk = "high"; recommendations.push("DMARC policy is p=none (monitoring only) — upgrade to p=quarantine then p=reject"); } else if (policy === "quarantine") { risk = "medium"; } dmarc = { found: true, record: dmarcRecord, policy, subdomain_policy: subdomainPolicy, rua, risk }; } else { recommendations.push("No DMARC record found — add _dmarc TXT record with at least p=quarantine"); dmarc = { found: false, risk: "critical" }; } } catch { recommendations.push("No DMARC record found — add _dmarc TXT record with at least p=quarantine"); dmarc = { found: false, risk: "critical" }; } // DKIM const dkimResults: EmailSecurityResult["dkim"] = []; for (const selector of selectors) { try { const txts = await dns.resolveTxt(`${selector}._domainkey.${domain}`); const record = txts.map((t) => t.join("")).join(""); let keySize: string | undefined; const pMatch = record.match(/p=([A-Za-z0-9+/=]+)/); if (pMatch) { const keyBytes = Math.ceil((pMatch[1].length * 3) / 4); const bits = keyBytes * 8; keySize = `${bits}-bit`; if (bits <= 1024) { recommendations.push(`DKIM selector "${selector}" uses ${bits}-bit key — upgrade to 2048-bit minimum`); } } dkimResults.push({ selector, found: true, record, keySize }); } catch { // Also try CNAME (common for Microsoft 365 DKIM) try { const cnames = await dns.resolveCname(`${selector}._domainkey.${domain}`); dkimResults.push({ selector, found: true, record: `CNAME → ${cnames[0]}` }); } catch { dkimResults.push({ selector, found: false }); } } } if (!dkimResults.some((d) => d.found)) { recommendations.push("No DKIM records found for any common selector — configure DKIM signing"); } // Overall risk const risks = [spf.risk, dmarc.risk]; let overallRisk: EmailSecurityResult["overallRisk"] = "low"; if (risks.includes("critical")) overallRisk = "critical"; else if (risks.includes("high")) overallRisk = "high"; else if (risks.includes("medium")) overallRisk = "medium"; return { domain, spf, dmarc, dkim: dkimResults, overallRisk, recommendations }; } - src/protocol/tools.ts:46-55 (registration)Tool definition and registration for dns_email_security in the protocol tools file.
const dnsEmailSecurityTool: ToolDef = { name: "dns_email_security", description: "Analyze email security posture: SPF, DMARC, DKIM records with risk assessment and recommendations. Checks common DKIM selectors (google, selector1, selector2, k1, etc.).", schema: { domain: z.string().describe("Domain to analyze"), dkim_selectors: z.array(z.string()).optional().describe("Custom DKIM selectors to check (default: common selectors)"), }, execute: async (args) => json(await dnsEmailSecurity(args.domain as string, args.dkim_selectors as string[] | undefined)), };