nodemailer 10.0.2 → 10.0.4
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/CHANGELOG.md +18 -0
- package/dist/cjs/fetch/cookies.d.ts +4 -2
- package/dist/cjs/fetch/cookies.js +28 -14
- package/dist/cjs/mail-composer/index.js +5 -2
- package/dist/cjs/package-info.d.ts +1 -1
- package/dist/cjs/package-info.js +1 -1
- package/dist/cjs/ses-transport/index.js +5 -1
- package/dist/cjs/well-known/services.js +2 -0
- package/dist/esm/fetch/cookies.d.ts +4 -2
- package/dist/esm/fetch/cookies.js +25 -14
- package/dist/esm/mail-composer/index.js +5 -2
- package/dist/esm/package-info.d.ts +1 -1
- package/dist/esm/package-info.js +1 -1
- package/dist/esm/ses-transport/index.js +5 -1
- package/dist/esm/well-known/services.js +2 -0
- package/dist/well-known/services.json +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## [10.0.4](https://github.com/nodemailer/nodemailer/compare/v10.0.3...v10.0.4) (2026-09-11)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **fetch:** scope a cookie without a Path to the RFC 6265 default path ([2f907cb](https://github.com/nodemailer/nodemailer/commit/2f907cb71396860d0b2534dc72c3d252ef16339d))
|
|
9
|
+
* **fetch:** send cookies set with Path back to the exact path ([#1861](https://github.com/nodemailer/nodemailer/issues/1861)) ([d557113](https://github.com/nodemailer/nodemailer/commit/d5571139750de47abbac73a3b07ae90ff39a7bdd))
|
|
10
|
+
* **mail-composer:** keep httpHeaders and tls for href alternatives and icalEvent ([#1862](https://github.com/nodemailer/nodemailer/issues/1862)) ([7f502be](https://github.com/nodemailer/nodemailer/commit/7f502bee4a6b0386099c66c0a35d92850ec462f7))
|
|
11
|
+
* resolve well-known services by their primary domains ([#1859](https://github.com/nodemailer/nodemailer/issues/1859)) ([085f525](https://github.com/nodemailer/nodemailer/commit/085f525e6b292f0ca32ab1b20d4a4ee82fc40e2d))
|
|
12
|
+
* **ses-transport:** throw a configuration error when the SES client is missing ([#1863](https://github.com/nodemailer/nodemailer/issues/1863)) ([4d9c4c9](https://github.com/nodemailer/nodemailer/commit/4d9c4c966aaca61ff6d7649a1ac0aa0ceac6ef1e))
|
|
13
|
+
|
|
14
|
+
## [10.0.3](https://github.com/nodemailer/nodemailer/compare/v10.0.2...v10.0.3) (2026-09-10)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* **fetch:** honor the cookie Domain attribute without accepting public suffixes ([1608391](https://github.com/nodemailer/nodemailer/commit/1608391ff4ccd5422a88e4cf760b26068aad6d39)), closes [#1856](https://github.com/nodemailer/nodemailer/issues/1856)
|
|
20
|
+
|
|
3
21
|
## [10.0.2](https://github.com/nodemailer/nodemailer/compare/v10.0.1...v10.0.2) (2026-09-09)
|
|
4
22
|
|
|
5
23
|
|
|
@@ -85,10 +85,12 @@ export default class Cookies {
|
|
|
85
85
|
*/
|
|
86
86
|
isExpired(cookie: Cookie): boolean;
|
|
87
87
|
/**
|
|
88
|
-
* Returns
|
|
88
|
+
* Returns the default path for an URL path argument, the default-path of
|
|
89
|
+
* RFC 6265 section 5.1.4. A cookie that carries no Path attribute is scoped
|
|
90
|
+
* to the directory of the URL it was set from
|
|
89
91
|
*
|
|
90
92
|
* @param pathname
|
|
91
|
-
* @returns
|
|
93
|
+
* @returns Default path
|
|
92
94
|
*/
|
|
93
95
|
getPath(pathname?: string | null): string;
|
|
94
96
|
}
|
|
@@ -33,7 +33,11 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
return result;
|
|
34
34
|
};
|
|
35
35
|
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
36
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const node_net_1 = __importDefault(require("node:net"));
|
|
37
41
|
const urllib = __importStar(require("../shared/url.js"));
|
|
38
42
|
const SESSION_TIMEOUT = 1800; // 30 min
|
|
39
43
|
/**
|
|
@@ -59,12 +63,19 @@ class Cookies {
|
|
|
59
63
|
let domain;
|
|
60
64
|
if (cookie.domain) {
|
|
61
65
|
domain = cookie.domain.replace(/^\./, '');
|
|
62
|
-
// do not allow cross origin cookies
|
|
66
|
+
// do not allow cross origin cookies. There is no public suffix list here, so a
|
|
67
|
+
// multi-label suffix like 'co.uk' can not be told apart from a registrable domain
|
|
63
68
|
if (
|
|
64
69
|
// can't be valid if the requested domain is shorter than current hostname
|
|
65
70
|
urlparts.hostname.length < domain.length ||
|
|
71
|
+
// a top level domain is not a valid scope, 'Domain=com' would otherwise be
|
|
72
|
+
// sent to every .com host. A trailing dot does not make 'com.' any better
|
|
73
|
+
domain.indexOf('.') < 0 ||
|
|
74
|
+
domain.endsWith('.') ||
|
|
75
|
+
// an IP address has no subdomains, so cookies set on it stay host-only
|
|
76
|
+
node_net_1.default.isIP(urlparts.hostname) ||
|
|
66
77
|
// prefix domains with dot to be sure that partial matches are not used
|
|
67
|
-
('.' + urlparts.hostname).
|
|
78
|
+
!('.' + urlparts.hostname).endsWith('.' + domain)) {
|
|
68
79
|
cookie.domain = urlparts.hostname;
|
|
69
80
|
}
|
|
70
81
|
}
|
|
@@ -184,9 +195,13 @@ class Cookies {
|
|
|
184
195
|
('.' + urlparts.hostname).substr(-cookie.domain.length) !== cookie.domain)) {
|
|
185
196
|
return false;
|
|
186
197
|
}
|
|
187
|
-
// check if path matches
|
|
188
|
-
|
|
189
|
-
|
|
198
|
+
// check if the request path path-matches the cookie path (RFC 6265 section 5.1.4):
|
|
199
|
+
// identical paths match, otherwise the cookie path must be a directory prefix
|
|
200
|
+
const pathname = urlparts.pathname || '/';
|
|
201
|
+
const cookiePath = cookie.path;
|
|
202
|
+
const pathMatches = pathname === cookiePath ||
|
|
203
|
+
(pathname.startsWith(cookiePath) && (cookiePath.endsWith('/') || pathname.charAt(cookiePath.length) === '/'));
|
|
204
|
+
if (!pathMatches) {
|
|
190
205
|
return false;
|
|
191
206
|
}
|
|
192
207
|
// check secure argument
|
|
@@ -243,22 +258,21 @@ class Cookies {
|
|
|
243
258
|
return (cookie.expires && cookie.expires < new Date()) || !cookie.value;
|
|
244
259
|
}
|
|
245
260
|
/**
|
|
246
|
-
* Returns
|
|
261
|
+
* Returns the default path for an URL path argument, the default-path of
|
|
262
|
+
* RFC 6265 section 5.1.4. A cookie that carries no Path attribute is scoped
|
|
263
|
+
* to the directory of the URL it was set from
|
|
247
264
|
*
|
|
248
265
|
* @param pathname
|
|
249
|
-
* @returns
|
|
266
|
+
* @returns Default path
|
|
250
267
|
*/
|
|
251
268
|
getPath(pathname) {
|
|
252
269
|
const pathParts = (pathname || '/').split('/');
|
|
253
270
|
pathParts.pop(); // remove filename part
|
|
254
|
-
|
|
255
|
-
//
|
|
271
|
+
const path = pathParts.join('/').trim();
|
|
272
|
+
// a path that holds no more than one '/' is scoped to the root path, and so
|
|
273
|
+
// is one that does not start with '/' at all
|
|
256
274
|
if (path.charAt(0) !== '/') {
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
// ensure path suffix /
|
|
260
|
-
if (path.substr(-1) !== '/') {
|
|
261
|
-
path += '/';
|
|
275
|
+
return '/';
|
|
262
276
|
}
|
|
263
277
|
return path;
|
|
264
278
|
}
|
|
@@ -253,7 +253,8 @@ class MailComposer {
|
|
|
253
253
|
else if (icalEvent.href) {
|
|
254
254
|
icalEvent.content = {
|
|
255
255
|
href: icalEvent.href,
|
|
256
|
-
httpHeaders: icalEvent.httpHeaders
|
|
256
|
+
httpHeaders: icalEvent.httpHeaders,
|
|
257
|
+
tls: icalEvent.tls
|
|
257
258
|
};
|
|
258
259
|
icalEvent.href = undefined;
|
|
259
260
|
}
|
|
@@ -362,7 +363,9 @@ class MailComposer {
|
|
|
362
363
|
}
|
|
363
364
|
else if (alternative.href) {
|
|
364
365
|
data.content = {
|
|
365
|
-
href: alternative.href
|
|
366
|
+
href: alternative.href,
|
|
367
|
+
httpHeaders: alternative.httpHeaders,
|
|
368
|
+
tls: alternative.tls
|
|
366
369
|
};
|
|
367
370
|
}
|
|
368
371
|
else {
|
package/dist/cjs/package-info.js
CHANGED
|
@@ -62,7 +62,11 @@ function tagSesError(err) {
|
|
|
62
62
|
class SESTransport extends node_events_1.default {
|
|
63
63
|
constructor(options) {
|
|
64
64
|
super();
|
|
65
|
-
options
|
|
65
|
+
if (!options || !options.SES || !options.SES.sesClient) {
|
|
66
|
+
const error = new Error('Missing SES configuration, expecting { sesClient, SendEmailCommand } from @aws-sdk/client-sesv2, see https://nodemailer.com/transports/ses/');
|
|
67
|
+
error.code = errors.ECONFIG;
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
66
70
|
this.options = options;
|
|
67
71
|
this.ses = this.options.SES;
|
|
68
72
|
this.name = 'SESTransport';
|
|
@@ -119,6 +119,7 @@ exports.services = {
|
|
|
119
119
|
"FastMail": {
|
|
120
120
|
"description": "FastMail",
|
|
121
121
|
"domains": [
|
|
122
|
+
"fastmail.com",
|
|
122
123
|
"fastmail.fm"
|
|
123
124
|
],
|
|
124
125
|
"host": "smtp.fastmail.com",
|
|
@@ -232,6 +233,7 @@ exports.services = {
|
|
|
232
233
|
"Mac"
|
|
233
234
|
],
|
|
234
235
|
"domains": [
|
|
236
|
+
"icloud.com",
|
|
235
237
|
"me.com",
|
|
236
238
|
"mac.com"
|
|
237
239
|
],
|
|
@@ -85,10 +85,12 @@ export default class Cookies {
|
|
|
85
85
|
*/
|
|
86
86
|
isExpired(cookie: Cookie): boolean;
|
|
87
87
|
/**
|
|
88
|
-
* Returns
|
|
88
|
+
* Returns the default path for an URL path argument, the default-path of
|
|
89
|
+
* RFC 6265 section 5.1.4. A cookie that carries no Path attribute is scoped
|
|
90
|
+
* to the directory of the URL it was set from
|
|
89
91
|
*
|
|
90
92
|
* @param pathname
|
|
91
|
-
* @returns
|
|
93
|
+
* @returns Default path
|
|
92
94
|
*/
|
|
93
95
|
getPath(pathname?: string | null): string;
|
|
94
96
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// module to handle cookies
|
|
2
|
+
import net from 'node:net';
|
|
2
3
|
import * as urllib from '../shared/url.js';
|
|
3
4
|
const SESSION_TIMEOUT = 1800; // 30 min
|
|
4
5
|
/**
|
|
@@ -24,12 +25,19 @@ export default class Cookies {
|
|
|
24
25
|
let domain;
|
|
25
26
|
if (cookie.domain) {
|
|
26
27
|
domain = cookie.domain.replace(/^\./, '');
|
|
27
|
-
// do not allow cross origin cookies
|
|
28
|
+
// do not allow cross origin cookies. There is no public suffix list here, so a
|
|
29
|
+
// multi-label suffix like 'co.uk' can not be told apart from a registrable domain
|
|
28
30
|
if (
|
|
29
31
|
// can't be valid if the requested domain is shorter than current hostname
|
|
30
32
|
urlparts.hostname.length < domain.length ||
|
|
33
|
+
// a top level domain is not a valid scope, 'Domain=com' would otherwise be
|
|
34
|
+
// sent to every .com host. A trailing dot does not make 'com.' any better
|
|
35
|
+
domain.indexOf('.') < 0 ||
|
|
36
|
+
domain.endsWith('.') ||
|
|
37
|
+
// an IP address has no subdomains, so cookies set on it stay host-only
|
|
38
|
+
net.isIP(urlparts.hostname) ||
|
|
31
39
|
// prefix domains with dot to be sure that partial matches are not used
|
|
32
|
-
('.' + urlparts.hostname).
|
|
40
|
+
!('.' + urlparts.hostname).endsWith('.' + domain)) {
|
|
33
41
|
cookie.domain = urlparts.hostname;
|
|
34
42
|
}
|
|
35
43
|
}
|
|
@@ -149,9 +157,13 @@ export default class Cookies {
|
|
|
149
157
|
('.' + urlparts.hostname).substr(-cookie.domain.length) !== cookie.domain)) {
|
|
150
158
|
return false;
|
|
151
159
|
}
|
|
152
|
-
// check if path matches
|
|
153
|
-
|
|
154
|
-
|
|
160
|
+
// check if the request path path-matches the cookie path (RFC 6265 section 5.1.4):
|
|
161
|
+
// identical paths match, otherwise the cookie path must be a directory prefix
|
|
162
|
+
const pathname = urlparts.pathname || '/';
|
|
163
|
+
const cookiePath = cookie.path;
|
|
164
|
+
const pathMatches = pathname === cookiePath ||
|
|
165
|
+
(pathname.startsWith(cookiePath) && (cookiePath.endsWith('/') || pathname.charAt(cookiePath.length) === '/'));
|
|
166
|
+
if (!pathMatches) {
|
|
155
167
|
return false;
|
|
156
168
|
}
|
|
157
169
|
// check secure argument
|
|
@@ -208,22 +220,21 @@ export default class Cookies {
|
|
|
208
220
|
return (cookie.expires && cookie.expires < new Date()) || !cookie.value;
|
|
209
221
|
}
|
|
210
222
|
/**
|
|
211
|
-
* Returns
|
|
223
|
+
* Returns the default path for an URL path argument, the default-path of
|
|
224
|
+
* RFC 6265 section 5.1.4. A cookie that carries no Path attribute is scoped
|
|
225
|
+
* to the directory of the URL it was set from
|
|
212
226
|
*
|
|
213
227
|
* @param pathname
|
|
214
|
-
* @returns
|
|
228
|
+
* @returns Default path
|
|
215
229
|
*/
|
|
216
230
|
getPath(pathname) {
|
|
217
231
|
const pathParts = (pathname || '/').split('/');
|
|
218
232
|
pathParts.pop(); // remove filename part
|
|
219
|
-
|
|
220
|
-
//
|
|
233
|
+
const path = pathParts.join('/').trim();
|
|
234
|
+
// a path that holds no more than one '/' is scoped to the root path, and so
|
|
235
|
+
// is one that does not start with '/' at all
|
|
221
236
|
if (path.charAt(0) !== '/') {
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
// ensure path suffix /
|
|
225
|
-
if (path.substr(-1) !== '/') {
|
|
226
|
-
path += '/';
|
|
237
|
+
return '/';
|
|
227
238
|
}
|
|
228
239
|
return path;
|
|
229
240
|
}
|
|
@@ -215,7 +215,8 @@ class MailComposer {
|
|
|
215
215
|
else if (icalEvent.href) {
|
|
216
216
|
icalEvent.content = {
|
|
217
217
|
href: icalEvent.href,
|
|
218
|
-
httpHeaders: icalEvent.httpHeaders
|
|
218
|
+
httpHeaders: icalEvent.httpHeaders,
|
|
219
|
+
tls: icalEvent.tls
|
|
219
220
|
};
|
|
220
221
|
icalEvent.href = undefined;
|
|
221
222
|
}
|
|
@@ -324,7 +325,9 @@ class MailComposer {
|
|
|
324
325
|
}
|
|
325
326
|
else if (alternative.href) {
|
|
326
327
|
data.content = {
|
|
327
|
-
href: alternative.href
|
|
328
|
+
href: alternative.href,
|
|
329
|
+
httpHeaders: alternative.httpHeaders,
|
|
330
|
+
tls: alternative.tls
|
|
328
331
|
};
|
|
329
332
|
}
|
|
330
333
|
else {
|
package/dist/esm/package-info.js
CHANGED
|
@@ -24,7 +24,11 @@ function tagSesError(err) {
|
|
|
24
24
|
class SESTransport extends EventEmitter {
|
|
25
25
|
constructor(options) {
|
|
26
26
|
super();
|
|
27
|
-
options
|
|
27
|
+
if (!options || !options.SES || !options.SES.sesClient) {
|
|
28
|
+
const error = new Error('Missing SES configuration, expecting { sesClient, SendEmailCommand } from @aws-sdk/client-sesv2, see https://nodemailer.com/transports/ses/');
|
|
29
|
+
error.code = errors.ECONFIG;
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
28
32
|
this.options = options;
|
|
29
33
|
this.ses = this.options.SES;
|
|
30
34
|
this.name = 'SESTransport';
|
|
@@ -116,6 +116,7 @@ export const services = {
|
|
|
116
116
|
"FastMail": {
|
|
117
117
|
"description": "FastMail",
|
|
118
118
|
"domains": [
|
|
119
|
+
"fastmail.com",
|
|
119
120
|
"fastmail.fm"
|
|
120
121
|
],
|
|
121
122
|
"host": "smtp.fastmail.com",
|
|
@@ -229,6 +230,7 @@ export const services = {
|
|
|
229
230
|
"Mac"
|
|
230
231
|
],
|
|
231
232
|
"domains": [
|
|
233
|
+
"icloud.com",
|
|
232
234
|
"me.com",
|
|
233
235
|
"mac.com"
|
|
234
236
|
],
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
|
|
108
108
|
"FastMail": {
|
|
109
109
|
"description": "FastMail",
|
|
110
|
-
"domains": ["fastmail.fm"],
|
|
110
|
+
"domains": ["fastmail.com", "fastmail.fm"],
|
|
111
111
|
"host": "smtp.fastmail.com",
|
|
112
112
|
"port": 465,
|
|
113
113
|
"secure": true
|
|
@@ -196,7 +196,7 @@
|
|
|
196
196
|
"iCloud": {
|
|
197
197
|
"description": "iCloud Mail",
|
|
198
198
|
"aliases": ["Me", "Mac"],
|
|
199
|
-
"domains": ["me.com", "mac.com"],
|
|
199
|
+
"domains": ["icloud.com", "me.com", "mac.com"],
|
|
200
200
|
"host": "smtp.mail.me.com",
|
|
201
201
|
"port": 587
|
|
202
202
|
},
|