nodemailer 7.0.9 → 7.0.11

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.
@@ -1,9 +1,9 @@
1
1
  {
2
- "packages": {
3
- ".": {
4
- "release-type": "node",
5
- "package-name": "nodemailer",
6
- "pull-request-title-pattern": "chore${scope}: release ${version} [skip-ci]"
2
+ "packages": {
3
+ ".": {
4
+ "release-type": "node",
5
+ "package-name": "nodemailer",
6
+ "pull-request-title-pattern": "chore${scope}: release ${version} [skip-ci]"
7
+ }
7
8
  }
8
- }
9
9
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## [7.0.11](https://github.com/nodemailer/nodemailer/compare/v7.0.10...v7.0.11) (2025-11-26)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * prevent stack overflow DoS in addressparser with deeply nested groups ([b61b9c0](https://github.com/nodemailer/nodemailer/commit/b61b9c0cfd682b6f647754ca338373b68336a150))
9
+
10
+ ## [7.0.10](https://github.com/nodemailer/nodemailer/compare/v7.0.9...v7.0.10) (2025-10-23)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * Increase data URI size limit from 100KB to 50MB and preserve content type ([28dbf3f](https://github.com/nodemailer/nodemailer/commit/28dbf3fe129653f5756c150a98dc40593bfb2cfe))
16
+
3
17
  ## [7.0.9](https://github.com/nodemailer/nodemailer/compare/v7.0.8...v7.0.9) (2025-10-07)
4
18
 
5
19
 
@@ -4,9 +4,10 @@
4
4
  * Converts tokens for a single address into an address object
5
5
  *
6
6
  * @param {Array} tokens Tokens object
7
+ * @param {Number} depth Current recursion depth for nested group protection
7
8
  * @return {Object} Address object
8
9
  */
9
- function _handleAddress(tokens) {
10
+ function _handleAddress(tokens, depth) {
10
11
  let isGroup = false;
11
12
  let state = 'text';
12
13
  let address;
@@ -87,7 +88,7 @@ function _handleAddress(tokens) {
87
88
  // Parse group members, but flatten any nested groups (RFC 5322 doesn't allow nesting)
88
89
  let groupMembers = [];
89
90
  if (data.group.length) {
90
- let parsedGroup = addressparser(data.group.join(','));
91
+ let parsedGroup = addressparser(data.group.join(','), { _depth: depth + 1 });
91
92
  // Flatten: if any member is itself a group, extract its members into the sequence
92
93
  parsedGroup.forEach(member => {
93
94
  if (member.group) {
@@ -299,6 +300,13 @@ class Tokenizer {
299
300
  }
300
301
  }
301
302
 
303
+ /**
304
+ * Maximum recursion depth for parsing nested groups.
305
+ * RFC 5322 doesn't allow nested groups, so this is a safeguard against
306
+ * malicious input that could cause stack overflow.
307
+ */
308
+ const MAX_NESTED_GROUP_DEPTH = 50;
309
+
302
310
  /**
303
311
  * Parses structured e-mail addresses from an address field
304
312
  *
@@ -311,10 +319,18 @@ class Tokenizer {
311
319
  * [{name: 'Name', address: 'address@domain'}]
312
320
  *
313
321
  * @param {String} str Address field
322
+ * @param {Object} options Optional options object
323
+ * @param {Number} options._depth Internal recursion depth counter (do not set manually)
314
324
  * @return {Array} An array of address objects
315
325
  */
316
326
  function addressparser(str, options) {
317
327
  options = options || {};
328
+ let depth = options._depth || 0;
329
+
330
+ // Prevent stack overflow from deeply nested groups (DoS protection)
331
+ if (depth > MAX_NESTED_GROUP_DEPTH) {
332
+ return [];
333
+ }
318
334
 
319
335
  let tokenizer = new Tokenizer(str);
320
336
  let tokens = tokenizer.tokenize();
@@ -339,7 +355,7 @@ function addressparser(str, options) {
339
355
  }
340
356
 
341
357
  addresses.forEach(address => {
342
- address = _handleAddress(address);
358
+ address = _handleAddress(address, depth);
343
359
  if (address.length) {
344
360
  parsedAddresses = parsedAddresses.concat(address);
345
361
  }
@@ -575,14 +575,27 @@ class MailComposer {
575
575
  return element;
576
576
  }
577
577
 
578
- if (dataUrl.length > 100000) {
579
- // 100KB limit for data URL string
578
+ if (dataUrl.length > 52428800) {
579
+ // 52428800 chars = 50MB limit for data URL string (~37.5MB decoded image)
580
+ // Extract content type before rejecting to preserve MIME type
581
+ let detectedType = 'application/octet-stream';
582
+ const commaPos = dataUrl.indexOf(',');
583
+
584
+ if (commaPos > 0 && commaPos < 200) {
585
+ // Parse header safely with size limit
586
+ const header = dataUrl.substring(5, commaPos); // skip 'data:'
587
+ const parts = header.split(';');
588
+ if (parts[0] && parts[0].includes('/')) {
589
+ detectedType = parts[0].trim();
590
+ }
591
+ }
592
+
580
593
  // Return empty content for excessively long data URLs
581
594
  return Object.assign({}, element, {
582
595
  path: false,
583
596
  href: false,
584
597
  content: Buffer.alloc(0),
585
- contentType: element.contentType || 'application/octet-stream'
598
+ contentType: element.contentType || detectedType
586
599
  });
587
600
  }
588
601
 
@@ -1147,8 +1147,8 @@ class SMTPConnection extends EventEmitter {
1147
1147
  }
1148
1148
  notify = notify.map(n => n.trim().toUpperCase());
1149
1149
  let validNotify = ['NEVER', 'SUCCESS', 'FAILURE', 'DELAY'];
1150
- let invaliNotify = notify.filter(n => !validNotify.includes(n));
1151
- if (invaliNotify.length || (notify.length > 1 && notify.includes('NEVER'))) {
1150
+ let invalidNotify = notify.filter(n => !validNotify.includes(n));
1151
+ if (invalidNotify.length || (notify.length > 1 && notify.includes('NEVER'))) {
1152
1152
  throw new Error('notify: ' + JSON.stringify(notify.join(',')));
1153
1153
  }
1154
1154
  notify = notify.join(',');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodemailer",
3
- "version": "7.0.9",
3
+ "version": "7.0.11",
4
4
  "description": "Easy as cake e-mail sending from your Node.js applications",
5
5
  "main": "lib/nodemailer.js",
6
6
  "scripts": {
@@ -26,20 +26,20 @@
26
26
  },
27
27
  "homepage": "https://nodemailer.com/",
28
28
  "devDependencies": {
29
- "@aws-sdk/client-sesv2": "3.901.0",
29
+ "@aws-sdk/client-sesv2": "3.940.0",
30
30
  "bunyan": "1.8.15",
31
31
  "c8": "10.1.3",
32
- "eslint": "^9.37.0",
33
- "eslint-config-prettier": "^10.1.8",
34
- "globals": "^16.4.0",
32
+ "eslint": "9.39.1",
33
+ "eslint-config-prettier": "10.1.8",
34
+ "globals": "16.5.0",
35
35
  "libbase64": "1.3.0",
36
36
  "libmime": "5.3.7",
37
37
  "libqp": "2.1.1",
38
38
  "nodemailer-ntlm-auth": "1.0.4",
39
- "prettier": "^3.6.2",
39
+ "prettier": "3.6.2",
40
40
  "proxy": "1.0.2",
41
41
  "proxy-test-server": "1.0.0",
42
- "smtp-server": "3.14.0"
42
+ "smtp-server": "3.16.1"
43
43
  },
44
44
  "engines": {
45
45
  "node": ">=6.0.0"