Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .changeset/wide-moons-shave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
"mailgrade": major
---

Rework the public interface around the calls people actually make, put a
letter on the grade, and ship the grade as a CLI.

**`npx mailgrade acme.com`.** The package now has a bin: the grade in a
terminal (letter, verdict, per-mechanism headlines, and the fixes) with
`--json` for scripts and `--strict` for CI, where a non-zero exit catches the
day someone "temporarily" weakens the DMARC record. Zero dependencies, like
everything else here.

**A letter on the grade.** `DomainGrade` now carries `letter`, `"A+"` down to
`"F"`. The verdict sets the band (enforcing, partial, nothing) and the details
move within it; `+all` is an automatic `F`. The rubric lives in the spec
corpus (`spec/grade.json`), so ports share it.

**One import.** `gradeDomain`, `verifyMessage` and `analyzeHeaders` are now on
`mailgrade` itself, so the common cases need no second import and no guessing
at a subpath. Every existing entry point still resolves; `mailgrade/doh` and
`mailgrade/verify` are now for narrowing a bundle, not for finding the API.

**No setup.** `resolver` is optional everywhere it was required, defaulting to
DNS over HTTPS. `verifyMessage(raw, { ip, sender })` works on its own, the way
`checkDomain` already did. A Node service that wants the system resolver still
passes `nodeResolver()` from `mailgrade/node-dns`.

**Headers you can use.** `verifyMessage` takes `mta` and returns `authResults`
and `headers`: a folded, CRLF-terminated `Received-SPF` and
`Authentication-Results` block, ready to prepend to the message. The values
embedded there arrive off the wire, so they are sanitised on the way in: a
CRLF in a hostile MAIL FROM becomes a space, never a forged header field. The
folder is quote-aware, so a legal `"a;b"@example.com` survives intact.

`DmarcAnalysis` also gains `reporting`: whether the record carries a `rua=`
tag, read with the same tag parsing as everything else.

**Names that match the package.** The headline call is `gradeDomain`, not
`checkDomain`: this library grades, and the README section has always been
called "Grade a domain". The pure function that used to hold that name is now
`gradeRecords`, which is what it always took; `gradeDomain(records)` named a
domain and accepted `DomainRecords`. The trio now reads in order:
`resolveDomain` -> `gradeRecords`, and `gradeDomain` for both at once.

Breaking: the JSON conformance corpus is no longer published in the tarball,
and the `mailgrade/spec/*` subpath is gone with it. The corpus stays in the
repository, which is where a port reads it from.

Breaking: the root barrel is now a curated list rather than `export *`, so
roughly 25 internals no longer appear on `mailgrade`. Each is still exported
from the entry point for its own area, so `blankCheck`, `editDistance`,
`parseHeaders` and `stripComments` move to `mailgrade/headers`, `txtChunks`
and `recordTags` to `mailgrade/dmarc`, and so on. `toAuthResults` keeps its
signature and now accepts any object with the five fields it reads. The
options type for `gradeDomain` and `resolveDomain` is now named
`GradeDomainOptions` (was `DohOptions`).
12 changes: 6 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

- [`e576838`](https://github.com/FallaxIO/mailgrade/commit/e576838dc1c305406f755bb4b8cb8aa3d58e2b02) Thanks [@IgnaceMaes](https://github.com/IgnaceMaes)! - First stable release.

No API changes from 0.2.0 — this marks the surface as settled and brings it
No API changes from 0.2.0; this marks the surface as settled and brings it
under semver: the grading, verification, DMARC record and header-analysis
entry points will not break without a major bump.

Expand All @@ -21,17 +21,17 @@
let a caller supply the real Public Suffix List.

The built-in suffix rules were a fixed list of 42 two-label pairs. A pair not
on it — `gov.uk`, `ac.jp`, `org.cn`, `com.ec` — collapsed every host under it
on it (`gov.uk`, `ac.jp`, `org.cn`, `com.ec`) collapsed every host under it
to the pair itself, so `dept.gov.uk` and `attacker.gov.uk` shared an
organizational domain and relaxed alignment reported them as aligned. In
`mailgrade/verify` that is a DMARC pass for a signature the From domain's
owner never made.

- The suffix rules now treat the registry's own labels (`co`, `com`, `gov`,
`ac`, `org`, `ne`, `sch`, …) as part of the suffix under any country code,
rather than only for enumerated pairs. The remaining approximation errs
toward reading a host as more specific than it is, which under-reports
alignment instead of over-reporting it.
`ac`, `org`, `ne`, `sch`, and so on) as part of the suffix under any
country code, rather than only for enumerated pairs. The remaining
approximation errs toward reading a host as more specific than it is,
which under-reports alignment instead of over-reporting it.
- `registrableDomain`, `domainLabel` and `aligns` take an optional
`{ publicSuffixes }`, and `verifyDmarc` and `verifyMessage` accept and
forward it. Supplying a list replaces the built-in rules entirely: the
Expand Down
Loading
Loading