nodemailer 10.0.7 → 10.0.8
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 +8 -0
- package/dist/cjs/mime-node/index.js +30 -20
- package/dist/cjs/package-info.d.ts +1 -1
- package/dist/cjs/package-info.js +1 -1
- package/dist/esm/mime-node/index.js +30 -20
- package/dist/esm/package-info.d.ts +1 -1
- package/dist/esm/package-info.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## [10.0.8](https://github.com/nodemailer/nodemailer/compare/v10.0.7...v10.0.8) (2026-09-11)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **mime-node:** clean the boundary where it is written, not only where it is built ([e14278d](https://github.com/nodemailer/nodemailer/commit/e14278d2dae9427280c79450605a27b1c5d2c355))
|
|
9
|
+
* **mime-node:** drop every control character from multipart boundary material ([a82a355](https://github.com/nodemailer/nodemailer/commit/a82a35554848a2e07485ff81f80aefe2736e5a04))
|
|
10
|
+
|
|
3
11
|
## [10.0.7](https://github.com/nodemailer/nodemailer/compare/v10.0.6...v10.0.7) (2026-09-11)
|
|
4
12
|
|
|
5
13
|
|
|
@@ -100,13 +100,21 @@ function normalizeDomain(domain, toUnicode) {
|
|
|
100
100
|
return toUnicode ? punycode.toUnicode(domain) : punycode.toASCII(domain);
|
|
101
101
|
}
|
|
102
102
|
/**
|
|
103
|
-
* Removes the
|
|
103
|
+
* Removes the characters that must never reach a multipart delimiter line.
|
|
104
|
+
*
|
|
105
|
+
* A line break splits the delimiter, so the boundary declared in the header can never
|
|
106
|
+
* match it again and the parts go out as body lines instead. The other C0 controls and
|
|
107
|
+
* DEL do not split anything but must not be written either: RFC 5321 does not allow a
|
|
108
|
+
* NUL in DATA at all, and an MTA or a scanner that stops at one reads a different
|
|
109
|
+
* message than a client that does not, which is the same parser disagreement a split
|
|
110
|
+
* delimiter creates. Both sides are cleaned together, since the declared value and the
|
|
111
|
+
* delimiters come from this one result.
|
|
104
112
|
*
|
|
105
113
|
* @param value Value to clean
|
|
106
|
-
* @return Value with every
|
|
114
|
+
* @return Value with every control character removed
|
|
107
115
|
*/
|
|
108
|
-
function
|
|
109
|
-
return value.replace(/[\
|
|
116
|
+
function _stripBoundaryControls(value) {
|
|
117
|
+
return value.replace(/[\x00-\x1f\x7f]+/g, '');
|
|
110
118
|
}
|
|
111
119
|
/**
|
|
112
120
|
* Creates a new mime tree node. Assumes 'multipart/*' as the content type
|
|
@@ -128,12 +136,11 @@ class MimeNode {
|
|
|
128
136
|
this.nodeCounter = 0;
|
|
129
137
|
options = options || {};
|
|
130
138
|
/**
|
|
131
|
-
* shared part of the unique multipart boundary.
|
|
132
|
-
*
|
|
133
|
-
* never match them again and the remainder would go out as body lines
|
|
139
|
+
* shared part of the unique multipart boundary. Control characters are dropped
|
|
140
|
+
* here rather than at the delimiter, see _stripBoundaryControls
|
|
134
141
|
*/
|
|
135
|
-
this.baseBoundary =
|
|
136
|
-
this.boundaryPrefix =
|
|
142
|
+
this.baseBoundary = _stripBoundaryControls(options.baseBoundary || node_crypto_1.default.randomBytes(8).toString('hex'));
|
|
143
|
+
this.boundaryPrefix = _stripBoundaryControls(options.boundaryPrefix || '--_NmP');
|
|
137
144
|
this.disableFileAccess = !!options.disableFileAccess;
|
|
138
145
|
this.disableUrlAccess = !!options.disableUrlAccess;
|
|
139
146
|
this.normalizeHeaderKey = options.normalizeHeaderKey;
|
|
@@ -1180,19 +1187,20 @@ class MimeNode {
|
|
|
1180
1187
|
this.contentType = structured.value.trim().toLowerCase();
|
|
1181
1188
|
this.multipart = /^multipart\//i.test(this.contentType) ? this.contentType.substr(this.contentType.indexOf('/') + 1) : false;
|
|
1182
1189
|
if (this.multipart) {
|
|
1183
|
-
//
|
|
1184
|
-
//
|
|
1185
|
-
//
|
|
1186
|
-
// delimiters are assigned from the same expression here, so whatever the
|
|
1187
|
-
// header emitter then does with it, the two sides cannot disagree.
|
|
1190
|
+
// The declared value and the delimiters are assigned from the same expression
|
|
1191
|
+
// here, so whatever the header emitter then does with it, the two sides cannot
|
|
1192
|
+
// disagree about which characters the boundary is made of.
|
|
1188
1193
|
//
|
|
1189
1194
|
// Stripping runs before the fallback rather than over the whole chain: a
|
|
1190
|
-
// boundary
|
|
1195
|
+
// boundary made only of control characters would otherwise strip to '' and
|
|
1191
1196
|
// leave the node declaring no boundary and streaming bare '--' delimiters.
|
|
1192
|
-
//
|
|
1193
|
-
//
|
|
1194
|
-
|
|
1195
|
-
|
|
1197
|
+
// _generateBoundary cleans what it builds, but this is the one place the boundary
|
|
1198
|
+
// is written, so it cleans the value it is about to write rather than trusting
|
|
1199
|
+
// where it came from. MimeNode is exported and subclassable, so the generator is
|
|
1200
|
+
// not necessarily the one below. Stripping twice costs nothing, it is idempotent.
|
|
1201
|
+
const declared = _stripBoundaryControls(structured.params.boundary || this.boundary || '');
|
|
1202
|
+
this.boundary = structured.params.boundary =
|
|
1203
|
+
declared || _stripBoundaryControls(this._generateBoundary());
|
|
1196
1204
|
}
|
|
1197
1205
|
else {
|
|
1198
1206
|
this.boundary = false;
|
|
@@ -1205,7 +1213,9 @@ class MimeNode {
|
|
|
1205
1213
|
* @internal
|
|
1206
1214
|
*/
|
|
1207
1215
|
_generateBoundary() {
|
|
1208
|
-
|
|
1216
|
+
// baseBoundary and boundaryPrefix are public, so they may hold something the
|
|
1217
|
+
// constructor never cleaned, and the -Part_ suffix keeps the result non empty
|
|
1218
|
+
return _stripBoundaryControls(this.rootNode.boundaryPrefix + '-' + this.rootNode.baseBoundary) + '-Part_' + this._nodeId;
|
|
1209
1219
|
}
|
|
1210
1220
|
/**
|
|
1211
1221
|
* Encodes a header value for use in the generated rfc2822 email.
|
package/dist/cjs/package-info.js
CHANGED
|
@@ -62,13 +62,21 @@ function normalizeDomain(domain, toUnicode) {
|
|
|
62
62
|
return toUnicode ? punycode.toUnicode(domain) : punycode.toASCII(domain);
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
|
-
* Removes the
|
|
65
|
+
* Removes the characters that must never reach a multipart delimiter line.
|
|
66
|
+
*
|
|
67
|
+
* A line break splits the delimiter, so the boundary declared in the header can never
|
|
68
|
+
* match it again and the parts go out as body lines instead. The other C0 controls and
|
|
69
|
+
* DEL do not split anything but must not be written either: RFC 5321 does not allow a
|
|
70
|
+
* NUL in DATA at all, and an MTA or a scanner that stops at one reads a different
|
|
71
|
+
* message than a client that does not, which is the same parser disagreement a split
|
|
72
|
+
* delimiter creates. Both sides are cleaned together, since the declared value and the
|
|
73
|
+
* delimiters come from this one result.
|
|
66
74
|
*
|
|
67
75
|
* @param value Value to clean
|
|
68
|
-
* @return Value with every
|
|
76
|
+
* @return Value with every control character removed
|
|
69
77
|
*/
|
|
70
|
-
function
|
|
71
|
-
return value.replace(/[\
|
|
78
|
+
function _stripBoundaryControls(value) {
|
|
79
|
+
return value.replace(/[\x00-\x1f\x7f]+/g, '');
|
|
72
80
|
}
|
|
73
81
|
/**
|
|
74
82
|
* Creates a new mime tree node. Assumes 'multipart/*' as the content type
|
|
@@ -90,12 +98,11 @@ class MimeNode {
|
|
|
90
98
|
this.nodeCounter = 0;
|
|
91
99
|
options = options || {};
|
|
92
100
|
/**
|
|
93
|
-
* shared part of the unique multipart boundary.
|
|
94
|
-
*
|
|
95
|
-
* never match them again and the remainder would go out as body lines
|
|
101
|
+
* shared part of the unique multipart boundary. Control characters are dropped
|
|
102
|
+
* here rather than at the delimiter, see _stripBoundaryControls
|
|
96
103
|
*/
|
|
97
|
-
this.baseBoundary =
|
|
98
|
-
this.boundaryPrefix =
|
|
104
|
+
this.baseBoundary = _stripBoundaryControls(options.baseBoundary || crypto.randomBytes(8).toString('hex'));
|
|
105
|
+
this.boundaryPrefix = _stripBoundaryControls(options.boundaryPrefix || '--_NmP');
|
|
99
106
|
this.disableFileAccess = !!options.disableFileAccess;
|
|
100
107
|
this.disableUrlAccess = !!options.disableUrlAccess;
|
|
101
108
|
this.normalizeHeaderKey = options.normalizeHeaderKey;
|
|
@@ -1142,19 +1149,20 @@ class MimeNode {
|
|
|
1142
1149
|
this.contentType = structured.value.trim().toLowerCase();
|
|
1143
1150
|
this.multipart = /^multipart\//i.test(this.contentType) ? this.contentType.substr(this.contentType.indexOf('/') + 1) : false;
|
|
1144
1151
|
if (this.multipart) {
|
|
1145
|
-
//
|
|
1146
|
-
//
|
|
1147
|
-
//
|
|
1148
|
-
// delimiters are assigned from the same expression here, so whatever the
|
|
1149
|
-
// header emitter then does with it, the two sides cannot disagree.
|
|
1152
|
+
// The declared value and the delimiters are assigned from the same expression
|
|
1153
|
+
// here, so whatever the header emitter then does with it, the two sides cannot
|
|
1154
|
+
// disagree about which characters the boundary is made of.
|
|
1150
1155
|
//
|
|
1151
1156
|
// Stripping runs before the fallback rather than over the whole chain: a
|
|
1152
|
-
// boundary
|
|
1157
|
+
// boundary made only of control characters would otherwise strip to '' and
|
|
1153
1158
|
// leave the node declaring no boundary and streaming bare '--' delimiters.
|
|
1154
|
-
//
|
|
1155
|
-
//
|
|
1156
|
-
|
|
1157
|
-
|
|
1159
|
+
// _generateBoundary cleans what it builds, but this is the one place the boundary
|
|
1160
|
+
// is written, so it cleans the value it is about to write rather than trusting
|
|
1161
|
+
// where it came from. MimeNode is exported and subclassable, so the generator is
|
|
1162
|
+
// not necessarily the one below. Stripping twice costs nothing, it is idempotent.
|
|
1163
|
+
const declared = _stripBoundaryControls(structured.params.boundary || this.boundary || '');
|
|
1164
|
+
this.boundary = structured.params.boundary =
|
|
1165
|
+
declared || _stripBoundaryControls(this._generateBoundary());
|
|
1158
1166
|
}
|
|
1159
1167
|
else {
|
|
1160
1168
|
this.boundary = false;
|
|
@@ -1167,7 +1175,9 @@ class MimeNode {
|
|
|
1167
1175
|
* @internal
|
|
1168
1176
|
*/
|
|
1169
1177
|
_generateBoundary() {
|
|
1170
|
-
|
|
1178
|
+
// baseBoundary and boundaryPrefix are public, so they may hold something the
|
|
1179
|
+
// constructor never cleaned, and the -Part_ suffix keeps the result non empty
|
|
1180
|
+
return _stripBoundaryControls(this.rootNode.boundaryPrefix + '-' + this.rootNode.baseBoundary) + '-Part_' + this._nodeId;
|
|
1171
1181
|
}
|
|
1172
1182
|
/**
|
|
1173
1183
|
* Encodes a header value for use in the generated rfc2822 email.
|
package/dist/esm/package-info.js
CHANGED