@aglyn/shared-util-email 1.0.0-beta.200 → 1.0.0-beta.202
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/index.d.ts +6 -0
- package/src/index.js +16 -0
- package/src/index.js.map +1 -1
- package/src/lib/email-deliverability.d.ts +200 -0
- package/src/lib/email-deliverability.js +461 -0
- package/src/lib/email-deliverability.js.map +1 -0
- package/src/lib/email-delivery-events.d.ts +16 -39
- package/src/lib/email-delivery-events.js +28 -12
- package/src/lib/email-delivery-events.js.map +1 -1
- package/src/lib/email-linkify.d.ts +24 -0
- package/src/lib/email-linkify.js +73 -0
- package/src/lib/email-linkify.js.map +1 -0
- package/src/lib/email-render.js +6 -1
- package/src/lib/email-render.js.map +1 -1
- package/src/lib/hosted-page-screen.d.ts +60 -0
- package/src/lib/hosted-page-screen.js +314 -0
- package/src/lib/hosted-page-screen.js.map +1 -0
- package/src/lib/mail-bounce.d.ts +42 -0
- package/src/lib/mail-bounce.js +108 -0
- package/src/lib/mail-bounce.js.map +1 -0
- package/src/lib/mail-gateway.d.ts +161 -0
- package/src/lib/mail-gateway.js +330 -0
- package/src/lib/mail-gateway.js.map +1 -0
- package/src/lib/outbound-phishing-screen.d.ts +248 -0
- package/src/lib/outbound-phishing-screen.js +769 -0
- package/src/lib/outbound-phishing-screen.js.map +1 -0
- package/src/lib/outbound-screen-gate.d.ts +95 -0
- package/src/lib/outbound-screen-gate.js +109 -0
- package/src/lib/outbound-screen-gate.js.map +1 -0
- package/src/lib/send-email.d.ts +79 -1
- package/src/lib/send-email.js +153 -10
- package/src/lib/send-email.js.map +1 -1
- package/src/lib/sending-domain.d.ts +22 -1
- package/src/lib/sending-domain.js +0 -95
- package/src/lib/sending-domain.js.map +1 -1
- package/src/lib/system-email-catalog.js +400 -0
- package/src/lib/system-email-catalog.js.map +1 -1
- package/src/lib/text-email-html.d.ts +38 -0
- package/src/lib/text-email-html.js +3 -44
- package/src/lib/text-email-html.js.map +1 -1
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/** One brand the screen recognizes. */
|
|
18
|
+
export interface PhishingScreenBrand {
|
|
19
|
+
/** Stable id, as stored on a hold. */
|
|
20
|
+
id: string;
|
|
21
|
+
/** How staff read it. */
|
|
22
|
+
label: string;
|
|
23
|
+
/** Copy that names the brand — subject, body, sender. Case-insensitive. */
|
|
24
|
+
mention: RegExp;
|
|
25
|
+
/**
|
|
26
|
+
* Host labels that name the brand. Matched against each dot-separated
|
|
27
|
+
* label of a link's host, whole or hyphen-joined, after leetspeak is
|
|
28
|
+
* folded — never as a bare substring, which is how `amazonaws` or
|
|
29
|
+
* `bookingengine` would read as a brand.
|
|
30
|
+
*/
|
|
31
|
+
hostTokens: readonly string[];
|
|
32
|
+
/**
|
|
33
|
+
* Registrable domains the brand genuinely links from. The FIRST is its
|
|
34
|
+
* primary domain, the one a kit embeds in a longer host.
|
|
35
|
+
*/
|
|
36
|
+
officialDomains: readonly string[];
|
|
37
|
+
/**
|
|
38
|
+
* The brand's name is an ordinary word (`apple`, `amazon`), so a host
|
|
39
|
+
* label names the brand only when it IS the word — `apple.example.top` —
|
|
40
|
+
* and never hyphen-joined or leet-folded, which `apple-orchard-farm.com`
|
|
41
|
+
* would otherwise be.
|
|
42
|
+
*/
|
|
43
|
+
wordToken?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Whether `<brand>.<country code>` (and `<brand>.co.<cc>`) is the brand's
|
|
46
|
+
* too — true for the marketplaces that run a site per country.
|
|
47
|
+
*/
|
|
48
|
+
countryDomains?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Registrable domains the brand runs but where OTHER people's content lives —
|
|
51
|
+
* the platform's own tenant apex, where every site is `<name>.<apex>`. A
|
|
52
|
+
* host there never wears the brand as a disguise, so the lookalike rule
|
|
53
|
+
* skips it; but it is not the brand's own domain either, so a link to a
|
|
54
|
+
* neighbor's site still counts as "somewhere else" to the lure rules, and a
|
|
55
|
+
* workspace hosted there does not own the brand by being hosted there.
|
|
56
|
+
*/
|
|
57
|
+
hostedDomains?: readonly string[];
|
|
58
|
+
}
|
|
59
|
+
export declare const PHISHING_SCREEN_BRANDS: readonly PhishingScreenBrand[];
|
|
60
|
+
/**
|
|
61
|
+
* Account-action and credential phrasing — the half of a lure that asks the
|
|
62
|
+
* reader to DO something with an account. Only ever counted beside a brand
|
|
63
|
+
* AND a foreign link; alone, several of these are ordinary transactional copy.
|
|
64
|
+
*/
|
|
65
|
+
export declare const PHISHING_LURE_PATTERNS: readonly RegExp[];
|
|
66
|
+
/** A signal the screen found. */
|
|
67
|
+
export type PhishingScreenSignal = {
|
|
68
|
+
code: 'lookalike-link';
|
|
69
|
+
brand: string;
|
|
70
|
+
host: string;
|
|
71
|
+
} | {
|
|
72
|
+
code: 'brand-sender';
|
|
73
|
+
brand: string;
|
|
74
|
+
fromName: string;
|
|
75
|
+
} | {
|
|
76
|
+
code: 'brand-lure-link';
|
|
77
|
+
brand: string;
|
|
78
|
+
lure: string;
|
|
79
|
+
host: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* A published page asks for a password, a card number or a one-time code
|
|
83
|
+
* in a field that is not one of the platform's own sign-in or checkout
|
|
84
|
+
* elements (`hosted-page-screen.ts`). Strong.
|
|
85
|
+
*/
|
|
86
|
+
| {
|
|
87
|
+
code: 'credential-field';
|
|
88
|
+
field: 'password' | 'card' | 'otp';
|
|
89
|
+
label: string;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A published page names a brand that is not the workspace's beside a
|
|
93
|
+
* sign-in, verify or payment call to action (`hosted-page-screen.ts`).
|
|
94
|
+
* Soft.
|
|
95
|
+
*/
|
|
96
|
+
| {
|
|
97
|
+
code: 'brand-action-page';
|
|
98
|
+
brand: string;
|
|
99
|
+
action: string;
|
|
100
|
+
};
|
|
101
|
+
export interface PhishingScreenInput {
|
|
102
|
+
subject?: string | null;
|
|
103
|
+
/** The display name in front of the sending address. */
|
|
104
|
+
fromName?: string | null;
|
|
105
|
+
/**
|
|
106
|
+
* The ADDRESS the message leaves from (AGL-3362) — a `From:` domain is a
|
|
107
|
+
* link the recipient never has to click, so its host is read by the
|
|
108
|
+
* lookalike rule exactly like a link's: mail from `support@paypa1.com` is
|
|
109
|
+
* the disguise whoever sends it. Not an own domain for the lure's "is this
|
|
110
|
+
* elsewhere" test either way; it only ever adds a lookalike signal.
|
|
111
|
+
*/
|
|
112
|
+
fromAddress?: string | null;
|
|
113
|
+
replyTo?: string | readonly string[] | null;
|
|
114
|
+
preheader?: string | null;
|
|
115
|
+
/**
|
|
116
|
+
* Everything else the recipient reads or clicks: the body, the plain-text
|
|
117
|
+
* part, a design's nodes serialized — any string. Links are found by
|
|
118
|
+
* shape, so a JSON-encoded design is as readable here as HTML.
|
|
119
|
+
*/
|
|
120
|
+
bodies?: readonly (string | null | undefined)[];
|
|
121
|
+
/**
|
|
122
|
+
* The workspace's own names — org, site, subdomain. A brand in one of them
|
|
123
|
+
* is the workspace's own, and `brand-sender` / `brand-lure-link` do not
|
|
124
|
+
* fire for it. The lookalike check does not consult names: a workspace
|
|
125
|
+
* NAMED "Poshmark" linking to `poshmark.id63835663.shop` is the incident.
|
|
126
|
+
*/
|
|
127
|
+
ownNames?: readonly (string | null | undefined)[];
|
|
128
|
+
/**
|
|
129
|
+
* The workspace's own hosts — its subdomain on the tenant apex, custom
|
|
130
|
+
* domain, sending domain. A link to one of them, or below one, is never
|
|
131
|
+
* the "somewhere else" of a lure. Matched by host, never by registrable
|
|
132
|
+
* domain: every site's subdomain shares the tenant apex, and a link to a
|
|
133
|
+
* NEIGHBOR's site is somewhere else.
|
|
134
|
+
*
|
|
135
|
+
* Not an exemption from the lookalike check. A host shaped like
|
|
136
|
+
* `paypal-secure.com` is a lookalike whoever attached it, and a workspace
|
|
137
|
+
* that attached it has attached the disguise.
|
|
138
|
+
*/
|
|
139
|
+
ownDomains?: readonly (string | null | undefined)[];
|
|
140
|
+
}
|
|
141
|
+
export interface PhishingScreenVerdict {
|
|
142
|
+
/** True when the message should be held for staff review. */
|
|
143
|
+
hold: boolean;
|
|
144
|
+
signals: PhishingScreenSignal[];
|
|
145
|
+
}
|
|
146
|
+
/** The registrable domain of a host: `a.b.evil.co.uk` → `evil.co.uk`. */
|
|
147
|
+
export declare function registrableDomain(host: string): string;
|
|
148
|
+
/** Every brand's real domains, for "is this link just the brand itself". */
|
|
149
|
+
declare function isAnyOfficialDomain(registrable: string): boolean;
|
|
150
|
+
/** The brand a host impersonates, or null. */
|
|
151
|
+
export declare function lookalikeBrandForHost(host: string): PhishingScreenBrand | null;
|
|
152
|
+
/** The hosts every link in the text points at, lowercased. */
|
|
153
|
+
export declare function linkHostsIn(text: string): string[];
|
|
154
|
+
/** Lowercased alphanumerics only, so "Wells-Fargo Advisors" contains `wellsfargo`. */
|
|
155
|
+
declare function squash(text: string): string;
|
|
156
|
+
/** Is this brand the workspace's own, by name or by a domain it holds? */
|
|
157
|
+
declare function isOwnBrand(brand: PhishingScreenBrand, ownNames: string, ownRegistrables: ReadonlySet<string>): boolean;
|
|
158
|
+
/** `squash`: lowercased alphanumerics, the form own-name matching reads. */
|
|
159
|
+
export declare const squashScreenText: typeof squash;
|
|
160
|
+
/** Is this brand the workspace's own, by name (squashed) or by a domain it holds? */
|
|
161
|
+
export declare const isWorkspaceOwnBrand: typeof isOwnBrand;
|
|
162
|
+
/** Is this registrable domain one of any listed brand's own? */
|
|
163
|
+
export declare const isAnyOfficialBrandDomain: typeof isAnyOfficialDomain;
|
|
164
|
+
/** Is this registrable domain a social, maps or review link merchants ordinarily carry? */
|
|
165
|
+
export declare function isCommonLinkDomain(registrable: string): boolean;
|
|
166
|
+
/**
|
|
167
|
+
* Screen one outbound message. See the module header for what holds and why.
|
|
168
|
+
*/
|
|
169
|
+
export declare function screenOutboundEmail(input: PhishingScreenInput): PhishingScreenVerdict;
|
|
170
|
+
/** The brand's label for a stored id, for the staff surface. */
|
|
171
|
+
export declare function phishingScreenBrandLabel(id: string): string;
|
|
172
|
+
/** One line per signal, in words staff read on the review row. */
|
|
173
|
+
export declare function describePhishingScreenSignals(signals: readonly PhishingScreenSignal[]): string[];
|
|
174
|
+
/** A workspace younger than this many days has the soft rules applied. */
|
|
175
|
+
export declare const OUTBOUND_REVIEW_YOUNG_DAYS = 14;
|
|
176
|
+
/** Which tier a signal belongs to. */
|
|
177
|
+
export type PhishingSignalTier = 'strong' | 'soft';
|
|
178
|
+
/** The signal codes that hold for every workspace. */
|
|
179
|
+
export declare const STRONG_PHISHING_SIGNAL_CODES: ReadonlySet<string>;
|
|
180
|
+
export declare function phishingSignalTier(signal: {
|
|
181
|
+
code: string;
|
|
182
|
+
}): PhishingSignalTier;
|
|
183
|
+
/** Is a workspace of this age (days, `null` = unreadable) screened by the soft rules? */
|
|
184
|
+
export declare function isYoungWorkspaceAge(ageDays: number | null | undefined): boolean;
|
|
185
|
+
export interface OutboundScreenPolicy {
|
|
186
|
+
/**
|
|
187
|
+
* The workspace's age in days, or `null` when its creation date cannot be
|
|
188
|
+
* read — an org that predates the field, which is an EXISTING customer and
|
|
189
|
+
* is not young.
|
|
190
|
+
*/
|
|
191
|
+
ageDays: number | null;
|
|
192
|
+
/**
|
|
193
|
+
* The recipient's own act made this message owed: a receipt for their
|
|
194
|
+
* order, a confirmation of their booking, a reset of their password. The
|
|
195
|
+
* soft rules never hold it. The lookalike rule still does — a receipt that
|
|
196
|
+
* links to a Poshmark lookalike is the disguise, not the receipt.
|
|
197
|
+
*/
|
|
198
|
+
owed?: boolean;
|
|
199
|
+
}
|
|
200
|
+
/** The signals that HOLD under the tiers, for this workspace and message. */
|
|
201
|
+
export declare function signalsThatHold<T extends {
|
|
202
|
+
code: string;
|
|
203
|
+
}>(signals: readonly T[], policy: OutboundScreenPolicy): T[];
|
|
204
|
+
/**
|
|
205
|
+
* The same brand, lookalike and tier logic applied to a HOST NAME a workspace
|
|
206
|
+
* asks for — a subdomain on the tenant apex, or a custom domain it attaches.
|
|
207
|
+
*
|
|
208
|
+
* Words a brand's impersonator hyphen-joins to its name. A subdomain that
|
|
209
|
+
* joins a brand's word to one of these (`booking-review`, `apple-support`,
|
|
210
|
+
* `amazon-account`) is the incident's shape even where the brand's word is
|
|
211
|
+
* an ordinary one — which the lookalike rule alone deliberately lets pass,
|
|
212
|
+
* so a salon can be `tanyas-booking` and a bakery `apple-pie-co`.
|
|
213
|
+
*/
|
|
214
|
+
export declare const BRAND_IMPERSONATION_WORDS: ReadonlySet<string>;
|
|
215
|
+
/**
|
|
216
|
+
* The brand a requested subdomain label wears, or null.
|
|
217
|
+
*
|
|
218
|
+
* Refused for EVERY workspace, at creation and at rename — the tier is
|
|
219
|
+
* strong, because nothing is lost by asking a real bakery to pick
|
|
220
|
+
* `apple-pie-co` instead of `apple-support`, and a brand's name on our apex
|
|
221
|
+
* is a disguise that outlives every later screen. Two shapes:
|
|
222
|
+
*
|
|
223
|
+
* 1. the label is a lookalike host label ({@link lookalikeBrandForHost} on
|
|
224
|
+
* `<label>.<apex>`): `poshmark`, `paypal-secure`, `paypa1`, `appleid`;
|
|
225
|
+
* 2. a brand's word hyphen-joined to an impersonation word
|
|
226
|
+
* ({@link BRAND_IMPERSONATION_WORDS}): `booking-review`, `apple-support`.
|
|
227
|
+
*/
|
|
228
|
+
export declare function brandForSubdomainLabel(label: string, apex?: string): PhishingScreenBrand | null;
|
|
229
|
+
/**
|
|
230
|
+
* The platform's own entry in {@link PHISHING_SCREEN_BRANDS} (AGL-3365). A
|
|
231
|
+
* workspace owns every other brand by carrying its name or domain; the
|
|
232
|
+
* platform's is owned only by the platform's staff, because a workspace NAMED
|
|
233
|
+
* for the platform is the cheapest disguise there is.
|
|
234
|
+
*/
|
|
235
|
+
export declare const PLATFORM_PHISHING_BRAND_ID = "aglyn";
|
|
236
|
+
/** The platform's brand entry. */
|
|
237
|
+
export declare function platformPhishingBrand(): PhishingScreenBrand;
|
|
238
|
+
/**
|
|
239
|
+
* The brand a display NAME claims to speak for (AGL-3365), or null.
|
|
240
|
+
*
|
|
241
|
+
* For names a stranger reads as an identity — a marketplace publisher, a
|
|
242
|
+
* listing's title — rather than copy. A name that merely mentions a brand
|
|
243
|
+
* ("Sync for PayPal", "Bookings for Airbnb hosts") passes: that is how an
|
|
244
|
+
* integration is named. A name that pairs the brand with a claim of
|
|
245
|
+
* officialness ("PayPal Support", "Aglyn Official Plugins") is the disguise.
|
|
246
|
+
*/
|
|
247
|
+
export declare function brandClaimedByName(name: string): PhishingScreenBrand | null;
|
|
248
|
+
export {};
|