links.md
title: "Links" description: "How MachineMail checks every link in inbound mail for mismatched text, lookalike and punycode domains, raw IPs, shorteners and brand impersonation." canonical: "https://machinemail.dev/docs/trust/links"
Links
How MachineMail checks every link in inbound mail for mismatched text, lookalike and punycode domains, raw IPs, shorteners and brand impersonation.
Source: https://machinemail.dev/docs/trust/links
Every link in a message gets an entry in trust.links. Links come from HTML anchors (<a> and <area>) and from bare http://, https:// and www. URLs in the text. Entries are deduplicated by target and text, up to 500 per message.
{
"href": "http://198.51.100.23/paypal/login.php?session=8f7a6c",
"text": "https://www.paypal.com/signin",
"domain": "198.51.100.23",
"display_mismatch": true,
"shortener": false,
"suspicious": true,
"reasons": [
"Unencrypted http link to a login/account page",
"Link points to a raw IP address (198.51.100.23)",
"PayPal login-style path on 198.51.100.23",
"Link text says www.paypal.com, href is 198.51.100.23"
]
}
Links are analysed as written. MachineMail does not fetch them, so final_url is not currently set and no request ever reaches the sender's server because of a check.
Mismatched text: display_mismatch
When the visible text of a link looks like a URL or a domain, its domain is compared with the real destination. display_mismatch is true, and the link counts as suspicious, when:
- the text names a different organisational domain than the target (
Link text says www.paypal.com, href is 198.51.100.23) - the text names a domain but the link goes through a URL shortener (
Link text says stripe.com, but it goes through shortener bit.ly) - the text and target share a domain only once lookalike letters are normalised (
Link text says paypal.com, href is a lookalike domain ...) - a
mailto:link shows one address or domain and mails another
Links through known email click-trackers (SendGrid, Mailchimp, Mandrill, HubSpot, Mailgun, Postmark, SparkPost, Customer.io, Substack, beehiiv, Amazon SES tracking, Proofpoint URL Defense, Outlook Safe Links and others) are expected to differ from their text and are not marked as mismatched.
Link text that is not a URL ("View it in your browser") is not compared. Subdomains of the same organisation (www.stripe.com text, pay.stripe.com target) match.
Structural red flags: suspicious
suspicious is true when any of these fire. Each adds a line to reasons:
| Signal | Example reason |
|---|---|
javascript: or vbscript: URL |
javascript: URL |
data: URL |
data: URL (can embed a full phishing page) |
| Unparseable URL | Malformed URL |
| Scheme other than http, https, ftp | Unusual scheme ... |
| Plain http to a login, verify, account, billing or password path | Unencrypted http link to a login/account page |
| Raw IPv4 or IPv6 host | Link points to a raw IP address (198.51.100.23) |
Userinfo before the host, as in https://paypal.com@evil.example |
URL contains userinfo before the host (the real host is evil.example) |
| Port other than 80, 443, 8080, 8443 | Non-standard port 8888 |
| Punycode or mixed-script host | Domain uses lookalike characters (pаypal.com imitates paypal.com) or Internationalised (punycode) domain ... |
| Four or more labels above the registrable domain | Excessive subdomains (7 labels) |
| Hostname over 60 characters | Very long hostname |
| A brand name in a domain the brand does not own | PayPal name used in a domain it does not own (xn--pypal-4ve.com) |
| A brand name in a login-style path on someone else's domain | PayPal login-style path on 198.51.100.23 |
| A link inside hidden HTML | Link is hidden from the human reader |
mailto:, tel:, sms:, cid: and # fragment links are not checked for structural flags.
Brand impersonation
MachineMail keeps a list of brands commonly impersonated in phishing and the domains each one really uses: PayPal, Microsoft, Office 365, Outlook, OneDrive, Google, Gmail, Apple, iCloud, Amazon, Netflix, Facebook, Instagram, WhatsApp, LinkedIn, Dropbox, DocuSign, Adobe, Chase, Wells Fargo, Bank of America, Stripe, Coinbase, Binance, MetaMask, DHL, FedEx, USPS, HMRC, GitHub, Okta, Salesforce, Slack, Zoom, Venmo and Wise.
A brand name inside a host it does not own (paypal-secure-login.example, microsoft.account-verify.example) is flagged, and so is the brand's name in a credential-style path on a foreign domain. Hosts are compared after lookalike letters are normalised, so a Cyrillic а does not hide the brand. The same list is used by the classifier to catch a display name such as PayPal <service@paypa1-security.com>.
Shorteners
shortener is true for about fifty URL shorteners, including bit.ly, t.co, tinyurl.com, goo.gl, ow.ly, is.gd, rebrand.ly, cutt.ly, dub.sh, lnkd.in, amzn.to and youtu.be. A shortener alone does not make a link suspicious, but it adds URL shortener hides the destination to the link's reasons, and the classifier counts shortened links against a message that also asks for credentials or payment. For the message as a whole, a link through a shortener is evidence: it makes the message at least suspicious, however well the sender authenticated.
First-party shorteners and sibling domains
Some organisations shorten links in their own mail: t.co for X, lnkd.in for LinkedIn, youtu.be and g.co for Google and YouTube, amzn.to and a.co for Amazon, fb.me for Facebook, aka.ms for Microsoft. When the message passed DMARC aligned with one of that organisation's own domains, and the link names its destination in a url, u, target, redirect or dest parameter on one of that organisation's own domains, the link is first-party: shortener is false and the reasons say First-party link shortener of x.com to help.x.com. The destination is decoded and checked in its place. A bare short link (t.co/abc, lnkd.in/abc, fb.me/abc, aka.ms/abc) is still a shortener even in the organisation's own mail, because these organisations relay other people's posts and messages and their shorteners wrap whatever link those carry. youtu.be, g.co, amzn.to and a.co only ever redirect to their organisation's own sites, so in that organisation's aligned mail a bare link through them is first-party too. So is a link whose named destination is on someone else's site, and the same shortener in mail from anyone else or in mail that did not pass DMARC aligned. goo.gl is never first-party: anyone could create goo.gl links until 2019.
The same organisations' sibling domains count as the sender's own for link checks: a twitter.com or twimg.com link in mail from x.com is not an off-domain link. A free mail domain such as gmail.com is never treated as one of these organisations.
How links feed the verdict
- Any link with
display_mismatchmakes the message at leastsuspicious. - The links component is 10% of the score: 1 minus 0.6 per mismatched link and 0.3 per other suspicious link, never below 0.
- Mismatched links and suspicious links raise
is_phishing. - Reasons for suspicious and mismatched links are copied into the top-level
trust.reasons, prefixed with the link's domain where needed.
Using link reports in an agent
An agent that browses should not follow a link from mail just because the text looks right:
const safeLinks = message.trust.links.filter((l) => !l.suspicious && !l.display_mismatch && !l.shortener);
Better still, only follow links from verified or known mail that the task needs, such as a sign-up confirmation you just triggered. See Using trust in agents.