Skip to content

t0kubetsu/mailvalidator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mailvalidator

Assess the complete mail security posture of any domain — from the command line or as a Python library.

mailvalidator checks MX, SPF, DMARC, DKIM, BIMI, TLSRPT, MTA-STS, SMTP diagnostics, deep TLS inspection, and 104 DNS blacklists in a single command. Results are colour-coded and graded against the NCSC-NL IT Security Guidelines for Transport Layer Security (TLS).

$ mailvalidator check example.com

Python Tests Coverage License


Contents


Features

Check Command What is verified
MX Records mailvalidator mx Authoritative NS query, priority ordering, duplicate detection
SMTP Diagnostics mailvalidator smtp TCP connect latency, banner, PTR record, open relay, STARTTLS
TLS Inspection (part of smtp) TLS 1.0–1.3 version probing, 34 cipher suites graded per NCSC-NL, cipher order enforcement, key exchange (ECDHE/DHE/RSA), CRIME compression, RFC 5746 renegotiation, certificate trust chain/domain match/expiry
SPF mailvalidator spf Record lookup, all-qualifier grading, recursive include/redirect resolution, RFC 7208 lookup-count limit
DMARC mailvalidator dmarc Policy grading (none/quarantine/reject), pct, sp, rua, ruf, adkim/aspf alignment
DKIM mailvalidator dkim Base-node (_domainkey.<domain>) RFC 2308 existence check
BIMI mailvalidator bimi Record lookup, logo URL (HTTPS + SVG), VMC authority evidence
TLSRPT mailvalidator tlsrpt RFC 8460 record lookup, rua scheme validation (mailto/HTTPS)
MTA-STS mailvalidator mta-sts DNS record + HTTPS policy file fetch, mode, max_age, MX entries
CAA (part of smtp) RFC 8659 hierarchy walk, issue/issuewild tags, iodef HTTPS enforcement
DANE / TLSA (part of smtp) TLSA existence, SHA-256/SHA-512 fingerprint match, rollover scheme
Blacklist mailvalidator blacklist 104 DNSBL zones in parallel, RFC 5782 §2.1 compliant
Full Report mailvalidator check All of the above in one command

Requirements


Installation

From source:

git clone https://github.com/t0kubetsu/mailvalidator
cd mailvalidator
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

As an editable package:

pip install -e .

The mailvalidator command is then available in your shell.


CLI Usage

Full report

# All checks — MX, SMTP/TLS, SPF, DMARC, DKIM, BIMI, TLSRPT, MTA-STS, blacklist
mailvalidator check example.com

# Skip SMTP and blacklist (faster, no outbound TCP port 25 needed)
mailvalidator check example.com --no-smtp --no-blacklist

# Non-standard SMTP port
mailvalidator check example.com --smtp-port 587

Individual checks

mailvalidator mx        example.com
mailvalidator spf       example.com
mailvalidator dmarc     example.com
mailvalidator dkim      example.com
mailvalidator bimi      example.com
mailvalidator tlsrpt    example.com
mailvalidator mta-sts   example.com

# SMTP + full TLS inspection against a specific host
mailvalidator smtp mail.example.com
mailvalidator smtp mail.example.com --port 587

# Blacklist check
mailvalidator blacklist 203.0.113.42
mailvalidator blacklist 203.0.113.42 --workers 100

Python API

Full assessment

from mailvalidator.assessor import assess
from mailvalidator.reporter import print_full_report

report = assess(
    "example.com",
    smtp_port=25,
    run_smtp=True,
    run_blacklist=True,
    progress_cb=print,   # optional: called with a status string before each step
)

print_full_report(report)

Individual checks

from mailvalidator.checks.spf       import check_spf
from mailvalidator.checks.dmarc     import check_dmarc
from mailvalidator.checks.dkim      import check_dkim
from mailvalidator.checks.bimi      import check_bimi
from mailvalidator.checks.tlsrpt    import check_tlsrpt
from mailvalidator.checks.mta_sts   import check_mta_sts
from mailvalidator.checks.mx        import check_mx
from mailvalidator.checks.blacklist import check_blacklist
from mailvalidator.checks.smtp      import check_smtp

spf   = check_spf("example.com")
dmarc = check_dmarc("example.com")
mx    = check_mx("example.com")
smtp  = check_smtp("mail.example.com", port=25)
bl    = check_blacklist("203.0.113.42")

Working with results

Every check function returns a result dataclass with a checks list of CheckResult objects:

result = check_spf("example.com")

for check in result.checks:
    print(check.name, check.status.value, check.value)
    for detail in check.details:
        print("  ", detail)

Status values: OK, WARNING, ERROR, INFO, NOT_FOUND, GOOD, SUFFICIENT, PHASE_OUT, INSUFFICIENT, NA.


TLS Grading

TLS checks follow the NCSC-NL IT Security Guidelines for Transport Layer Security (TLS).

Grade Criteria Examples
Good Forward-secret AEAD cipher + strong key exchange All TLS 1.3 suites, ECDHE-RSA-AES256-GCM-SHA384
Sufficient Forward-secret but CBC mode or DHE overhead ECDHE-RSA-AES256-SHA384, DHE-RSA-AES256-GCM-SHA384
Phase-out No forward secrecy or weak block cipher RSA key exchange ciphers, 3DES (Sweet32)
Insufficient Broken or unsafe NULL, anonymous, export, RC4

TLS versions: TLS 1.3 → OK · TLS 1.2 → Sufficient · TLS 1.1/1.0 → Phase-out.

Beyond cipher grading, the SMTP check also verifies:

  • Server cipher-preference enforcement per version
  • Prescribed cipher ordering (Good → Sufficient → Phase-out)
  • EC curve and DH group strength (key exchange)
  • Key-exchange hash function (SHA-1/MD5 flagged)
  • TLS-layer compression (CRIME, CVE-2012-4929)
  • Secure renegotiation (RFC 5746)
  • Certificate trust chain, public key strength, signature algorithm, domain match, and expiry
  • CAA records (RFC 8659)
  • DANE/TLSA records with fingerprint verification and rollover scheme assessment

DNSBL Blacklist Check

104 DNS blacklist zones are queried in parallel using a ThreadPoolExecutor. A positive listing is confirmed only when the DNS response is exactly 127.0.0.2 (RFC 5782 §2.1 standard "listed" response).

Other 127.0.0.x return codes used by reputation or allowlist zones — for example 127.255.255.255 from query.bondedsender.org — are intentionally ignored to prevent false positives for IPs that are not actually blacklisted.


Project Structure

mailvalidator/
├── mailvalidator/
│   ├── __init__.py        Package version
│   ├── models.py          Dataclass result models + Status enum
│   ├── dns_utils.py       DNS resolver helpers
│   ├── assessor.py        High-level API — orchestrates all checks
│   ├── reporter.py        Rich terminal output
│   ├── cli.py             Typer CLI entry point
│   └── checks/
│       ├── mx.py
│       ├── smtp.py        SMTP diagnostics + deep TLS inspection
│       ├── spf.py
│       ├── dmarc.py
│       ├── dkim.py
│       ├── bimi.py
│       ├── tlsrpt.py
│       ├── mta_sts.py
│       └── blacklist.py   104 DNSBL zones
├── tests/
│   ├── conftest.py        Shared fixtures and factories
│   ├── test_init.py
│   ├── test_dns_utils.py
│   ├── test_assessor.py
│   ├── test_reporter.py
│   ├── test_cli.py
│   └── checks/
│       ├── test_mx.py
│       ├── test_smtp.py
│       ├── test_spf.py
│       ├── test_dmarc.py
│       ├── test_dkim.py
│       ├── test_bimi.py
│       ├── test_tlsrpt.py
│       ├── test_mta_sts.py
│       └── test_blacklist.py
├── requirements-dev.txt
├── requirements.txt
├── LICENSE
└── pyproject.toml

Running Tests

source .venv/bin/activate

# Run all tests
pytest tests/

# Run a single module
pytest tests/checks/test_smtp.py

# Run a single test class
pytest tests/checks/test_spf.py::TestSPFCoverage -v

The test suite has 291 tests and achieves 100% coverage of all testable code. SMTP network I/O functions (_probe_tls, check_smtp, etc.) require a live mail server and are excluded from unit tests via # pragma: no cover; integration tests against a real server are out of scope for the unit suite.


License

GPLv3 — see LICENSE for details.

About

Assess the complete mail security posture of any domain — from the command line or as a Python library.

Resources

License

Stars

Watchers

Forks

Contributors

Languages