nodemailer 9.0.4 → 9.0.6

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.
@@ -7,10 +7,15 @@ const util = require('util');
7
7
  const fs = require('fs');
8
8
  const nmfetch = require('../fetch');
9
9
  const errors = require('../errors');
10
+ const objects = require('./objects');
10
11
  const dns = require('dns');
11
12
  const net = require('net');
12
13
  const os = require('os');
13
14
 
15
+ // re-exported for the callers that already depend on this module, see ./objects
16
+ const isProtoKey = (module.exports.isProtoKey = objects.isProtoKey);
17
+ module.exports.copyOwnKeys = objects.copyOwnKeys;
18
+
14
19
  const DNS_TTL = 5 * 60 * 1000;
15
20
  const CACHE_CLEANUP_INTERVAL = 30 * 1000; // Minimum 30 seconds between cleanups
16
21
  const MAX_CACHE_SIZE = 1000; // Maximum number of entries in cache
@@ -355,7 +360,9 @@ module.exports.parseConnectionUrl = str => {
355
360
  return;
356
361
  }
357
362
 
358
- if (!(lKey in obj)) {
363
+ // `in` already keeps "__proto__" out, but only as a side effect of it being an
364
+ // Object.prototype member. Say it, so the protection survives a change to the check
365
+ if (!isProtoKey(lKey) && !(lKey in obj)) {
359
366
  obj[lKey] = value;
360
367
  }
361
368
  });
@@ -470,7 +477,7 @@ module.exports.parseDataURI = uri => {
470
477
  // Ensure there's a key before the '='
471
478
  const key = entry.substring(0, sepPos).trim();
472
479
  const value = entry.substring(sepPos + 1).trim();
473
- if (key) {
480
+ if (key && !isProtoKey(key)) {
474
481
  params[key] = value;
475
482
  }
476
483
  }
@@ -561,19 +568,24 @@ function resolveContentValue(data, key, options, callback) {
561
568
  }
562
569
  callback(null, value);
563
570
  });
564
- } else if (/^https?:\/\//i.test(content.path || content.href)) {
571
+ } else if (/^data:/i.test(content.path || content.href)) {
572
+ const parsedDataUri = module.exports.parseDataURI(content.path || content.href);
573
+
574
+ return callback(null, parsedDataUri && parsedDataUri.data ? parsedDataUri.data : Buffer.alloc(0));
575
+ } else if (content.href || /^https?:\/\//i.test(content.path)) {
576
+ // An href is always a URL, and so is a path that looks like one. Let nmfetch
577
+ // decide whether it is fetchable, it validates the parsed URL. Testing the raw
578
+ // string here instead would let a file: href fall through to the "return as is"
579
+ // default below and travel on inside the resolved message.
580
+ const url = content.href || content.path;
565
581
  if (options.disableUrlAccess) {
566
582
  return setImmediate(() => {
567
- const err = new Error('Url access rejected for ' + (content.path || content.href));
583
+ const err = new Error('Url access rejected for ' + url);
568
584
  err.code = errors.EURLACCESS;
569
585
  callback(err);
570
586
  });
571
587
  }
572
- return resolveStream(nmfetch(content.path || content.href, { headers: content.httpHeaders, tls: content.tls }), callback);
573
- } else if (/^data:/i.test(content.path || content.href)) {
574
- const parsedDataUri = module.exports.parseDataURI(content.path || content.href);
575
-
576
- return callback(null, parsedDataUri && parsedDataUri.data ? parsedDataUri.data : Buffer.alloc(0));
588
+ return resolveStream(nmfetch(url, { headers: content.httpHeaders, tls: content.tls }), callback);
577
589
  } else if (content.path) {
578
590
  if (options.disableFileAccess) {
579
591
  return setImmediate(() => {
@@ -603,10 +615,14 @@ module.exports.assign = function (/* target, ... sources */) {
603
615
 
604
616
  args.forEach(source => {
605
617
  Object.keys(source || {}).forEach(key => {
618
+ if (isProtoKey(key)) {
619
+ return;
620
+ }
606
621
  if (['tls', 'auth'].includes(key) && source[key] && typeof source[key] === 'object') {
607
622
  // tls and auth are special keys that need to be enumerated separately
608
- // other objects are passed as is
609
- target[key] = Object.assign(target[key] || {}, source[key]);
623
+ // other objects are passed as is. Enumerating is a copy of user supplied
624
+ // keys just like the loop above, so it gets the same treatment
625
+ target[key] = module.exports.copyOwnKeys(target[key] || {}, source[key]);
610
626
  } else {
611
627
  target[key] = source[key];
612
628
  }
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ // Safe copying of objects whose keys come from the caller.
4
+ //
5
+ // This lives in its own leaf module, like ./url.js, so that every layer can reach it.
6
+ // lib/shared/index.js requires lib/fetch, so lib/fetch can not require lib/shared back,
7
+ // and lib/mime-funcs is a leaf that would otherwise pull in dns/net/os/fs for a string
8
+ // comparison. lib/shared/index.js re-exports both functions for the callers that already
9
+ // depend on it.
10
+
11
+ /**
12
+ * Detects a key that can not be copied onto a plain object with `target[key] = value`.
13
+ *
14
+ * "__proto__" is the only one: assigning it runs the inherited setter and replaces the
15
+ * prototype of the target instead of adding a property to it, so a caller can smuggle
16
+ * values past validation that only inspects own keys. JSON.parse produces such a key
17
+ * where an object literal can not. "constructor" and "prototype" have no such setter and
18
+ * become ordinary own properties, so dropping them would only discard legitimate values.
19
+ *
20
+ * @param {String} key Key to check
21
+ * @returns {Boolean} true if the key must not be copied
22
+ */
23
+ module.exports.isProtoKey = key => key === '__proto__';
24
+
25
+ /**
26
+ * Copies own enumerable keys from a source object to a target object. Every copy that
27
+ * walks the keys of user supplied data goes through here, see isProtoKey.
28
+ *
29
+ * @param {Object} target Object to copy the keys to
30
+ * @param {Object} source Object to copy the keys from
31
+ * @param {Function} [skip] Optional predicate, return true to leave a key out
32
+ * @returns {Object} The target object
33
+ */
34
+ module.exports.copyOwnKeys = (target, source, skip) => {
35
+ Object.keys(source || {}).forEach(key => {
36
+ if (module.exports.isProtoKey(key) || (skip && skip(key))) {
37
+ return;
38
+ }
39
+ target[key] = source[key];
40
+ });
41
+ return target;
42
+ };
@@ -42,7 +42,7 @@ class StreamTransport {
42
42
  // We probably need this in the output
43
43
  mail.message.keepBcc = true;
44
44
 
45
- const envelope = mail.data.envelope || mail.message.getEnvelope();
45
+ const envelope = mail.message.getEnvelope();
46
46
  const messageId = mail.message.messageId();
47
47
 
48
48
  const recipients = [].concat(envelope.to || []);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodemailer",
3
- "version": "9.0.4",
3
+ "version": "9.0.6",
4
4
  "description": "Easy as cake e-mail sending from your Node.js applications",
5
5
  "main": "lib/nodemailer.js",
6
6
  "scripts": {
@@ -27,19 +27,19 @@
27
27
  },
28
28
  "homepage": "https://nodemailer.com/",
29
29
  "devDependencies": {
30
- "@aws-sdk/client-sesv2": "3.1096.0",
30
+ "@aws-sdk/client-sesv2": "3.1119.0",
31
31
  "bunyan": "1.8.15",
32
32
  "c8": "12.0.0",
33
- "eslint": "10.8.0",
33
+ "eslint": "10.9.1",
34
34
  "eslint-config-prettier": "10.1.8",
35
- "globals": "17.8.0",
35
+ "globals": "17.11.0",
36
36
  "libbase64": "1.3.0",
37
- "libmime": "5.4.1",
37
+ "libmime": "5.4.2",
38
38
  "libqp": "2.1.1",
39
39
  "prettier": "3.9.6",
40
40
  "proxy": "1.0.2",
41
41
  "proxy-test-server": "1.0.0",
42
- "smtp-server": "3.19.2"
42
+ "smtp-server": "3.19.3"
43
43
  },
44
44
  "engines": {
45
45
  "node": ">=6.0.0"