nodemailer 6.9.8 → 6.9.10

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/.ncurc.js CHANGED
@@ -2,8 +2,6 @@ module.exports = {
2
2
  upgrade: true,
3
3
  reject: [
4
4
  // API changes break existing tests
5
- 'proxy',
6
- // ESM
7
- 'chai'
5
+ 'proxy'
8
6
  ]
9
7
  };
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## [6.9.10](https://github.com/nodemailer/nodemailer/compare/v6.9.9...v6.9.10) (2024-02-22)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **data-uri:** Do not use regular expressions for parsing data URI schemes ([12e65e9](https://github.com/nodemailer/nodemailer/commit/12e65e975d80efe6bafe6de4590829b3b5ebb492))
9
+ * **data-uri:** Moved all data-uri regexes to use the non-regex parseDataUri method ([edd5dfe](https://github.com/nodemailer/nodemailer/commit/edd5dfe5ce9b725f8b8ae2830797f65b2a2b0a33))
10
+
11
+ ## [6.9.9](https://github.com/nodemailer/nodemailer/compare/v6.9.8...v6.9.9) (2024-02-01)
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * **security:** Fix issues described in GHSA-9h6g-pr28-7cqp. Do not use eternal matching pattern if only a few occurences are expected ([dd8f5e8](https://github.com/nodemailer/nodemailer/commit/dd8f5e8a4ddc99992e31df76bcff9c590035cd4a))
17
+ * **tests:** Use native node test runner, added code coverage support, removed grunt ([#1604](https://github.com/nodemailer/nodemailer/issues/1604)) ([be45c1b](https://github.com/nodemailer/nodemailer/commit/be45c1b299d012358d69247019391a02734d70af))
18
+
3
19
  ## [6.9.8](https://github.com/nodemailer/nodemailer/compare/v6.9.7...v6.9.8) (2023-12-30)
4
20
 
5
21
 
@@ -4,6 +4,7 @@
4
4
 
5
5
  const MimeNode = require('../mime-node');
6
6
  const mimeFuncs = require('../mime-funcs');
7
+ const parseDataURI = require('../shared').parseDataURI;
7
8
 
8
9
  /**
9
10
  * Creates the object for composing a MimeNode instance out from the mail options
@@ -537,12 +538,17 @@ class MailComposer {
537
538
  * @return {Object} Parsed element
538
539
  */
539
540
  _processDataUrl(element) {
540
- let parts = (element.path || element.href).match(/^data:((?:[^;]*;)*(?:[^,]*)),(.*)$/i);
541
- if (!parts) {
541
+ let parsedDataUri;
542
+ if ((element.path || element.href).match(/^data:/)) {
543
+ parsedDataUri = parseDataURI(element.path || element.href);
544
+ }
545
+
546
+ if (!parsedDataUri) {
542
547
  return element;
543
548
  }
544
549
 
545
- element.content = /\bbase64$/i.test(parts[1]) ? Buffer.from(parts[2], 'base64') : Buffer.from(decodeURIComponent(parts[2]));
550
+ element.content = parsedDataUri.data;
551
+ element.contentType = element.contentType || parsedDataUri.contentType;
546
552
 
547
553
  if ('path' in element) {
548
554
  element.path = false;
@@ -552,12 +558,6 @@ class MailComposer {
552
558
  element.href = false;
553
559
  }
554
560
 
555
- parts[1].split(';').forEach(item => {
556
- if (/^\w+\/[^/]+$/i.test(item)) {
557
- element.contentType = element.contentType || item.toLowerCase();
558
- }
559
- });
560
-
561
561
  return element;
562
562
  }
563
563
  }
@@ -395,21 +395,23 @@ class Mail extends EventEmitter {
395
395
  return callback(err);
396
396
  }
397
397
  let cidCounter = 0;
398
- html = (html || '').toString().replace(/(<img\b[^>]* src\s*=[\s"']*)(data:([^;]+);[^"'>\s]+)/gi, (match, prefix, dataUri, mimeType) => {
399
- let cid = crypto.randomBytes(10).toString('hex') + '@localhost';
400
- if (!mail.data.attachments) {
401
- mail.data.attachments = [];
402
- }
403
- if (!Array.isArray(mail.data.attachments)) {
404
- mail.data.attachments = [].concat(mail.data.attachments || []);
405
- }
406
- mail.data.attachments.push({
407
- path: dataUri,
408
- cid,
409
- filename: 'image-' + ++cidCounter + '.' + mimeTypes.detectExtension(mimeType)
398
+ html = (html || '')
399
+ .toString()
400
+ .replace(/(<img\b[^<>]{0,1024} src\s{0,20}=[\s"']{0,20})(data:([^;]+);[^"'>\s]+)/gi, (match, prefix, dataUri, mimeType) => {
401
+ let cid = crypto.randomBytes(10).toString('hex') + '@localhost';
402
+ if (!mail.data.attachments) {
403
+ mail.data.attachments = [];
404
+ }
405
+ if (!Array.isArray(mail.data.attachments)) {
406
+ mail.data.attachments = [].concat(mail.data.attachments || []);
407
+ }
408
+ mail.data.attachments.push({
409
+ path: dataUri,
410
+ cid,
411
+ filename: 'image-' + ++cidCounter + '.' + mimeTypes.detectExtension(mimeType)
412
+ });
413
+ return prefix + 'cid:' + cid;
410
414
  });
411
- return prefix + 'cid:' + cid;
412
- });
413
415
  mail.data.html = html;
414
416
  callback();
415
417
  });
@@ -418,6 +418,55 @@ module.exports.callbackPromise = (resolve, reject) =>
418
418
  }
419
419
  };
420
420
 
421
+ module.exports.parseDataURI = uri => {
422
+ let input = uri;
423
+ let commaPos = input.indexOf(',');
424
+ if (!commaPos) {
425
+ return uri;
426
+ }
427
+
428
+ let data = input.substring(commaPos + 1);
429
+ let metaStr = input.substring('data:'.length, commaPos);
430
+
431
+ let encoding;
432
+
433
+ let metaEntries = metaStr.split(';');
434
+ let lastMetaEntry = metaEntries.length > 1 ? metaEntries[metaEntries.length - 1] : false;
435
+ if (lastMetaEntry && lastMetaEntry.indexOf('=') < 0) {
436
+ encoding = lastMetaEntry.toLowerCase();
437
+ metaEntries.pop();
438
+ }
439
+
440
+ let contentType = metaEntries.shift() || 'application/octet-stream';
441
+ let params = {};
442
+ for (let entry of metaEntries) {
443
+ let sep = entry.indexOf('=');
444
+ if (sep >= 0) {
445
+ let key = entry.substring(0, sep);
446
+ let value = entry.substring(sep + 1);
447
+ params[key] = value;
448
+ }
449
+ }
450
+
451
+ switch (encoding) {
452
+ case 'base64':
453
+ data = Buffer.from(data, 'base64');
454
+ break;
455
+ case 'utf8':
456
+ data = Buffer.from(data);
457
+ break;
458
+ default:
459
+ try {
460
+ data = Buffer.from(decodeURIComponent(data));
461
+ } catch (err) {
462
+ data = Buffer.from(data);
463
+ }
464
+ data = Buffer.from(data);
465
+ }
466
+
467
+ return { data, encoding, contentType, params };
468
+ };
469
+
421
470
  /**
422
471
  * Resolves a String or a Buffer value for content value. Useful if the value
423
472
  * is a Stream or a file or an URL. If the value is a Stream, overwrites
@@ -470,11 +519,12 @@ module.exports.resolveContent = (data, key, callback) => {
470
519
  contentStream = nmfetch(content.path || content.href);
471
520
  return resolveStream(contentStream, callback);
472
521
  } else if (/^data:/i.test(content.path || content.href)) {
473
- let parts = (content.path || content.href).match(/^data:((?:[^;]*;)*(?:[^,]*)),(.*)$/i);
474
- if (!parts) {
522
+ let parsedDataUri = module.exports.parseDataURI(content.path || content.href);
523
+
524
+ if (!parsedDataUri || !parsedDataUri.data) {
475
525
  return callback(null, Buffer.from(0));
476
526
  }
477
- return callback(null, /\bbase64$/i.test(parts[1]) ? Buffer.from(parts[2], 'base64') : Buffer.from(decodeURIComponent(parts[2])));
527
+ return callback(null, parsedDataUri.data);
478
528
  } else if (content.path) {
479
529
  return resolveStream(fs.createReadStream(content.path), callback);
480
530
  }
package/package.json CHANGED
@@ -1,10 +1,12 @@
1
1
  {
2
2
  "name": "nodemailer",
3
- "version": "6.9.8",
3
+ "version": "6.9.10",
4
4
  "description": "Easy as cake e-mail sending from your Node.js applications",
5
5
  "main": "lib/nodemailer.js",
6
6
  "scripts": {
7
- "test": "grunt --trace-warnings",
7
+ "test": "node --test --test-concurrency=1 test/**/*.test.js test/**/*-test.js",
8
+ "test:coverage": "c8 node --test --test-concurrency=1 test/**/*.test.js test/**/*-test.js",
9
+ "lint": "eslint .",
8
10
  "update": "rm -rf node_modules/ package-lock.json && ncu -u && npm install"
9
11
  },
10
12
  "repository": {
@@ -23,21 +25,16 @@
23
25
  "devDependencies": {
24
26
  "@aws-sdk/client-ses": "3.484.0",
25
27
  "bunyan": "1.8.15",
26
- "chai": "4.3.10",
28
+ "c8": "8.0.1",
29
+ "eslint": "8.56.0",
27
30
  "eslint-config-nodemailer": "1.2.0",
28
31
  "eslint-config-prettier": "9.1.0",
29
- "grunt": "1.6.1",
30
- "grunt-cli": "1.4.3",
31
- "grunt-eslint": "24.3.0",
32
- "grunt-mocha-test": "0.13.3",
33
32
  "libbase64": "1.2.1",
34
33
  "libmime": "5.2.1",
35
34
  "libqp": "2.0.1",
36
- "mocha": "10.2.0",
37
35
  "nodemailer-ntlm-auth": "1.0.4",
38
36
  "proxy": "1.0.2",
39
37
  "proxy-test-server": "1.0.0",
40
- "sinon": "17.0.1",
41
38
  "smtp-server": "3.13.0"
42
39
  },
43
40
  "engines": {