nodemailer 9.1.0 → 9.1.1
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 +9 -0
- package/README.md +1 -1
- package/SECURITY.md +2 -2
- package/lib/mailer/mail-message.js +28 -2
- package/lib/mime-node/index.js +30 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## [9.1.1](https://github.com/nodemailer/nodemailer/compare/v9.1.0...v9.1.1) (2026-09-01)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **mailer:** apply the message access policy in resolveContent ([dc48ed3](https://github.com/nodemailer/nodemailer/commit/dc48ed395c4d6c79ee5c95eb6eff17bafe391474))
|
|
9
|
+
* **mailer:** keep message data from reopening the access sandbox ([ab7ef34](https://github.com/nodemailer/nodemailer/commit/ab7ef348b9a97b1fd70e7bfbeb56d4ea4a07946b))
|
|
10
|
+
* **mime-node:** inherit the access policy from the tree a node hangs in ([262d550](https://github.com/nodemailer/nodemailer/commit/262d550b1e121e3ff4ef6675d6771a5b2b4ddcec))
|
|
11
|
+
|
|
3
12
|
## [9.1.0](https://github.com/nodemailer/nodemailer/compare/v9.0.6...v9.1.0) (2026-08-31)
|
|
4
13
|
|
|
5
14
|
|
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ let configOptions = {
|
|
|
52
52
|
|
|
53
53
|
#### I have issues with DNS / hosts file
|
|
54
54
|
|
|
55
|
-
Node.js uses [c-ares](https://
|
|
55
|
+
Node.js uses [c-ares](https://github.com/c-ares/c-ares) to resolve domain names, not the DNS library provided by the system, so if you have some custom DNS routing set up, it might be ignored. Nodemailer runs [dns.resolve4()](https://nodejs.org/dist/latest-v16.x/docs/api/dns.html#dnsresolve4hostname-options-callback) and [dns.resolve6()](https://nodejs.org/dist/latest-v16.x/docs/api/dns.html#dnsresolve6hostname-options-callback) to resolve hostname into an IP address. If both calls fail, then Nodemailer will fall back to [dns.lookup()](https://nodejs.org/dist/latest-v16.x/docs/api/dns.html#dnslookuphostname-options-callback). If this does not work for you, you can hard code the IP address into the configuration like shown below. In that case, Nodemailer would not perform any DNS lookups.
|
|
56
56
|
|
|
57
57
|
```js
|
|
58
58
|
let configOptions = {
|
package/SECURITY.md
CHANGED
|
@@ -11,8 +11,8 @@ supported way to receive security updates.
|
|
|
11
11
|
|
|
12
12
|
| Version | Supported |
|
|
13
13
|
| ------- | ------------------ |
|
|
14
|
-
|
|
|
15
|
-
| <
|
|
14
|
+
| 9.x | :white_check_mark: |
|
|
15
|
+
| < 9.0 | :x: |
|
|
16
16
|
|
|
17
17
|
If you are on an older major, please upgrade. See the migration notes at
|
|
18
18
|
<https://nodemailer.com/> before updating.
|
|
@@ -36,10 +36,36 @@ class MailMessage {
|
|
|
36
36
|
this.data[key] = options[key];
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
|
+
|
|
40
|
+
// The access flags are a sandbox rather than a message field, so `defaults` counts as
|
|
41
|
+
// transporter configuration for them. For a transporter plugin it is the only channel
|
|
42
|
+
// there is, createTransport leaves `options` undefined for one, and the defaults copy
|
|
43
|
+
// above yields to anything the message already set, which let message data switch the
|
|
44
|
+
// sandbox back off. Closing is one way here, same as in resolveContent below: either
|
|
45
|
+
// side may switch a flag on, neither can switch off what the other closed.
|
|
46
|
+
['disableFileAccess', 'disableUrlAccess'].forEach(key => {
|
|
47
|
+
if (!(key in options) && hasOwn(defaults, key)) {
|
|
48
|
+
this.data[key] = this.data[key] || defaults[key];
|
|
49
|
+
}
|
|
50
|
+
});
|
|
39
51
|
}
|
|
40
52
|
|
|
41
|
-
resolveContent(
|
|
42
|
-
|
|
53
|
+
resolveContent(data, key, options, callback) {
|
|
54
|
+
// Most plugins call this with the legacy (data, key, callback) signature, which carries
|
|
55
|
+
// no access policy. The policy belongs to the message, so apply it here. Explicit
|
|
56
|
+
// options may only tighten it, never reopen what the transporter closed.
|
|
57
|
+
if (!callback && typeof options === 'function') {
|
|
58
|
+
callback = options;
|
|
59
|
+
options = false;
|
|
60
|
+
}
|
|
61
|
+
options = options || {};
|
|
62
|
+
|
|
63
|
+
const policy = {
|
|
64
|
+
disableFileAccess: this.data.disableFileAccess || options.disableFileAccess,
|
|
65
|
+
disableUrlAccess: this.data.disableUrlAccess || options.disableUrlAccess
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return shared.resolveContent(data, key, policy, callback);
|
|
43
69
|
}
|
|
44
70
|
|
|
45
71
|
resolveAll(callback) {
|
package/lib/mime-node/index.js
CHANGED
|
@@ -244,6 +244,14 @@ class MimeNode {
|
|
|
244
244
|
* @return {Object} Appended node object
|
|
245
245
|
*/
|
|
246
246
|
appendChild(childNode) {
|
|
247
|
+
// Take the node out of the tree it is in first. Leaving it there keeps it in that
|
|
248
|
+
// parent's childNodes, so it still streams as part of the old tree while parentNode
|
|
249
|
+
// already points at the new one, and anything read off the parent chain answers for
|
|
250
|
+
// the wrong tree.
|
|
251
|
+
if (childNode.parentNode && childNode.parentNode !== this) {
|
|
252
|
+
childNode.remove();
|
|
253
|
+
}
|
|
254
|
+
|
|
247
255
|
if (childNode.rootNode !== this.rootNode) {
|
|
248
256
|
childNode.rootNode = this.rootNode;
|
|
249
257
|
childNode._nodeId = ++this.rootNode.nodeCounter;
|
|
@@ -1026,6 +1034,26 @@ class MimeNode {
|
|
|
1026
1034
|
|
|
1027
1035
|
/////// PRIVATE METHODS
|
|
1028
1036
|
|
|
1037
|
+
/**
|
|
1038
|
+
* Checks an access policy flag for this node and every node above it. The flags are set
|
|
1039
|
+
* from the options the node was built with, and createChild only ever sees the options
|
|
1040
|
+
* the caller passed, so a child of a closed tree starts out open. Reading the answer off
|
|
1041
|
+
* the parent chain keeps it right whatever order the tree was assembled in.
|
|
1042
|
+
*
|
|
1043
|
+
* @param {String} flag Either 'disableFileAccess' or 'disableUrlAccess'
|
|
1044
|
+
* @return {Boolean} true if this node or an ancestor closed that access
|
|
1045
|
+
*/
|
|
1046
|
+
_accessDisabled(flag) {
|
|
1047
|
+
let node = this;
|
|
1048
|
+
while (node) {
|
|
1049
|
+
if (node[flag]) {
|
|
1050
|
+
return true;
|
|
1051
|
+
}
|
|
1052
|
+
node = node.parentNode;
|
|
1053
|
+
}
|
|
1054
|
+
return false;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1029
1057
|
/**
|
|
1030
1058
|
* Detects and returns handle to a stream related with the content.
|
|
1031
1059
|
*
|
|
@@ -1056,7 +1084,7 @@ class MimeNode {
|
|
|
1056
1084
|
}
|
|
1057
1085
|
|
|
1058
1086
|
if (content && typeof content.path === 'string' && !content.href) {
|
|
1059
|
-
if (this.disableFileAccess) {
|
|
1087
|
+
if (this._accessDisabled('disableFileAccess')) {
|
|
1060
1088
|
contentStream = new PassThrough();
|
|
1061
1089
|
setImmediate(() => {
|
|
1062
1090
|
const err = new Error('File access rejected for ' + content.path);
|
|
@@ -1070,7 +1098,7 @@ class MimeNode {
|
|
|
1070
1098
|
}
|
|
1071
1099
|
|
|
1072
1100
|
if (content && typeof content.href === 'string') {
|
|
1073
|
-
if (this.disableUrlAccess) {
|
|
1101
|
+
if (this._accessDisabled('disableUrlAccess')) {
|
|
1074
1102
|
contentStream = new PassThrough();
|
|
1075
1103
|
setImmediate(() => {
|
|
1076
1104
|
const err = new Error('Url access rejected for ' + content.href);
|