From 1369c5b90d1bb51b1095261e8184c7f58b4a8ea1 Mon Sep 17 00:00:00 2001 From: Dawid Dziurla Date: Mon, 15 Jun 2026 07:32:52 +0200 Subject: [PATCH] node_modules: update (#297) Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com> --- node_modules/.package-lock.json | 12 +- .../brace-expansion/dist/commonjs/index.js | 2 +- .../dist/commonjs/index.js.map | 2 +- .../brace-expansion/dist/esm/index.js | 2 +- .../brace-expansion/dist/esm/index.js.map | 2 +- node_modules/brace-expansion/package.json | 2 +- node_modules/nodemailer/CHANGELOG.md | 47 ++++++ node_modules/nodemailer/CLAUDE.md | 1 + node_modules/nodemailer/SECURITY.md | 65 ++++++++ node_modules/nodemailer/lib/fetch/cookies.js | 2 +- node_modules/nodemailer/lib/fetch/index.js | 29 +++- .../nodemailer/lib/mail-composer/index.js | 100 ++++++++---- node_modules/nodemailer/lib/mailer/index.js | 60 ++++--- .../nodemailer/lib/mailer/mail-message.js | 50 +++--- .../nodemailer/lib/mime-funcs/mime-types.js | 2 +- .../nodemailer/lib/mime-node/index.js | 2 +- node_modules/nodemailer/lib/nodemailer.js | 33 +++- .../lib/sendmail-transport/index.js | 16 +- .../nodemailer/lib/ses-transport/index.js | 26 ++- node_modules/nodemailer/lib/shared/index.js | 55 +++++-- node_modules/nodemailer/lib/shared/url.js | 151 ++++++++++++++++++ .../lib/smtp-connection/http-proxy-client.js | 21 ++- .../nodemailer/lib/smtp-connection/index.js | 49 ++++-- .../nodemailer/lib/smtp-transport/index.js | 49 ++++-- .../nodemailer/lib/stream-transport/index.js | 9 ++ node_modules/nodemailer/lib/xoauth2/index.js | 16 +- node_modules/nodemailer/package.json | 16 +- 27 files changed, 662 insertions(+), 159 deletions(-) create mode 100644 node_modules/nodemailer/SECURITY.md create mode 100644 node_modules/nodemailer/lib/shared/url.js diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index 191f9baa..245bd98e 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -58,9 +58,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -94,9 +94,9 @@ } }, "node_modules/nodemailer": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.7.tgz", - "integrity": "sha512-pkjE4mkBzQjdJT4/UmlKl3pX0rC9fZmjh7c6C9o7lv66Ac6w9WCnzPzhbPNxwZAzlF4mdq4CSWB5+FbK6FWCow==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-9.0.0.tgz", + "integrity": "sha512-tbPTid7d/p9jAA8CRZ3iomvrMaST0o6NYuY7v6JQZHpPRZ61mLFSPKYd7342NtOFuej9/+L48SOIxwfu2uDvtw==", "license": "MIT-0", "engines": { "node": ">=6.0.0" diff --git a/node_modules/brace-expansion/dist/commonjs/index.js b/node_modules/brace-expansion/dist/commonjs/index.js index b9f3c6fd..33063dd3 100644 --- a/node_modules/brace-expansion/dist/commonjs/index.js +++ b/node_modules/brace-expansion/dist/commonjs/index.js @@ -155,7 +155,7 @@ function expand_(str, max, isTop) { } const pad = n.some(isPadded); N = []; - for (let i = x; test(i, y); i += incr) { + for (let i = x; test(i, y) && N.length < max; i += incr) { let c; if (isAlphaSequence) { c = String.fromCharCode(i); diff --git a/node_modules/brace-expansion/dist/commonjs/index.js.map b/node_modules/brace-expansion/dist/commonjs/index.js.map index c9d15111..77dd0803 100644 --- a/node_modules/brace-expansion/dist/commonjs/index.js.map +++ b/node_modules/brace-expansion/dist/commonjs/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { balanced } from 'balanced-match'\n\nconst escSlash = '\\0SLASH' + Math.random() + '\\0'\nconst escOpen = '\\0OPEN' + Math.random() + '\\0'\nconst escClose = '\\0CLOSE' + Math.random() + '\\0'\nconst escComma = '\\0COMMA' + Math.random() + '\\0'\nconst escPeriod = '\\0PERIOD' + Math.random() + '\\0'\nconst escSlashPattern = new RegExp(escSlash, 'g')\nconst escOpenPattern = new RegExp(escOpen, 'g')\nconst escClosePattern = new RegExp(escClose, 'g')\nconst escCommaPattern = new RegExp(escComma, 'g')\nconst escPeriodPattern = new RegExp(escPeriod, 'g')\nconst slashPattern = /\\\\\\\\/g\nconst openPattern = /\\\\{/g\nconst closePattern = /\\\\}/g\nconst commaPattern = /\\\\,/g\nconst periodPattern = /\\\\\\./g\n\nexport const EXPANSION_MAX = 100_000\n\nfunction numeric(str: string) {\n return !isNaN(str as any) ? parseInt(str, 10) : str.charCodeAt(0)\n}\n\nfunction escapeBraces(str: string) {\n return str\n .replace(slashPattern, escSlash)\n .replace(openPattern, escOpen)\n .replace(closePattern, escClose)\n .replace(commaPattern, escComma)\n .replace(periodPattern, escPeriod)\n}\n\nfunction unescapeBraces(str: string) {\n return str\n .replace(escSlashPattern, '\\\\')\n .replace(escOpenPattern, '{')\n .replace(escClosePattern, '}')\n .replace(escCommaPattern, ',')\n .replace(escPeriodPattern, '.')\n}\n\n/**\n * Basically just str.split(\",\"), but handling cases\n * where we have nested braced sections, which should be\n * treated as individual members, like {a,{b,c},d}\n */\nfunction parseCommaParts(str: string) {\n if (!str) {\n return ['']\n }\n\n const parts: string[] = []\n const m = balanced('{', '}', str)\n\n if (!m) {\n return str.split(',')\n }\n\n const { pre, body, post } = m\n const p = pre.split(',')\n\n p[p.length - 1] += '{' + body + '}'\n const postParts = parseCommaParts(post)\n if (post.length) {\n ;(p[p.length - 1] as string) += postParts.shift()\n p.push.apply(p, postParts)\n }\n\n parts.push.apply(parts, p)\n\n return parts\n}\n\nexport type BraceExpansionOptions = {\n max?: number\n}\n\nexport function expand(str: string, options: BraceExpansionOptions = {}) {\n if (!str) {\n return []\n }\n\n const { max = EXPANSION_MAX } = options\n\n // I don't know why Bash 4.3 does this, but it does.\n // Anything starting with {} will have the first two bytes preserved\n // but *only* at the top level, so {},a}b will not expand to anything,\n // but a{},b}c will be expanded to [a}c,abc].\n // One could argue that this is a bug in Bash, but since the goal of\n // this module is to match Bash's rules, we escape a leading {}\n if (str.slice(0, 2) === '{}') {\n str = '\\\\{\\\\}' + str.slice(2)\n }\n\n return expand_(escapeBraces(str), max, true).map(unescapeBraces)\n}\n\nfunction embrace(str: string) {\n return '{' + str + '}'\n}\n\nfunction isPadded(el: string) {\n return /^-?0\\d/.test(el)\n}\n\nfunction lte(i: number, y: number) {\n return i <= y\n}\n\nfunction gte(i: number, y: number) {\n return i >= y\n}\n\nfunction expand_(str: string, max: number, isTop: boolean): string[] {\n /** @type {string[]} */\n const expansions: string[] = []\n\n const m = balanced('{', '}', str)\n if (!m) return [str]\n\n // no need to expand pre, since it is guaranteed to be free of brace-sets\n const pre = m.pre\n const post: string[] = m.post.length ? expand_(m.post, max, false) : ['']\n\n if (/\\$$/.test(m.pre)) {\n for (let k = 0; k < post.length && k < max; k++) {\n const expansion = pre + '{' + m.body + '}' + post[k]\n expansions.push(expansion)\n }\n } else {\n const isNumericSequence = /^-?\\d+\\.\\.-?\\d+(?:\\.\\.-?\\d+)?$/.test(m.body)\n const isAlphaSequence = /^[a-zA-Z]\\.\\.[a-zA-Z](?:\\.\\.-?\\d+)?$/.test(\n m.body,\n )\n const isSequence = isNumericSequence || isAlphaSequence\n const isOptions = m.body.indexOf(',') >= 0\n if (!isSequence && !isOptions) {\n // {a},b}\n if (m.post.match(/,(?!,).*\\}/)) {\n str = m.pre + '{' + m.body + escClose + m.post\n return expand_(str, max, true)\n }\n return [str]\n }\n\n let n: string[]\n if (isSequence) {\n n = m.body.split(/\\.\\./)\n } else {\n n = parseCommaParts(m.body)\n if (n.length === 1 && n[0] !== undefined) {\n // x{{a,b}}y ==> x{a}y x{b}y\n n = expand_(n[0], max, false).map(embrace)\n //XXX is this necessary? Can't seem to hit it in tests.\n /* c8 ignore start */\n if (n.length === 1) {\n return post.map(p => m.pre + n[0] + p)\n }\n /* c8 ignore stop */\n }\n }\n\n // at this point, n is the parts, and we know it's not a comma set\n // with a single entry.\n let N: string[]\n\n if (isSequence && n[0] !== undefined && n[1] !== undefined) {\n const x = numeric(n[0])\n const y = numeric(n[1])\n const width = Math.max(n[0].length, n[1].length)\n let incr =\n n.length === 3 && n[2] !== undefined ?\n Math.max(Math.abs(numeric(n[2])), 1)\n : 1\n let test = lte\n const reverse = y < x\n if (reverse) {\n incr *= -1\n test = gte\n }\n const pad = n.some(isPadded)\n\n N = []\n\n for (let i = x; test(i, y); i += incr) {\n let c\n if (isAlphaSequence) {\n c = String.fromCharCode(i)\n if (c === '\\\\') {\n c = ''\n }\n } else {\n c = String(i)\n if (pad) {\n const need = width - c.length\n if (need > 0) {\n const z = new Array(need + 1).join('0')\n if (i < 0) {\n c = '-' + z + c.slice(1)\n } else {\n c = z + c\n }\n }\n }\n }\n N.push(c)\n }\n } else {\n N = []\n\n for (let j = 0; j < n.length; j++) {\n N.push.apply(N, expand_(n[j] as string, max, false))\n }\n }\n\n for (let j = 0; j < N.length; j++) {\n for (let k = 0; k < post.length && expansions.length < max; k++) {\n const expansion = pre + N[j] + post[k]\n if (!isTop || isSequence || expansion) {\n expansions.push(expansion)\n }\n }\n }\n }\n\n return expansions\n}\n"]} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { balanced } from 'balanced-match'\n\nconst escSlash = '\\0SLASH' + Math.random() + '\\0'\nconst escOpen = '\\0OPEN' + Math.random() + '\\0'\nconst escClose = '\\0CLOSE' + Math.random() + '\\0'\nconst escComma = '\\0COMMA' + Math.random() + '\\0'\nconst escPeriod = '\\0PERIOD' + Math.random() + '\\0'\nconst escSlashPattern = new RegExp(escSlash, 'g')\nconst escOpenPattern = new RegExp(escOpen, 'g')\nconst escClosePattern = new RegExp(escClose, 'g')\nconst escCommaPattern = new RegExp(escComma, 'g')\nconst escPeriodPattern = new RegExp(escPeriod, 'g')\nconst slashPattern = /\\\\\\\\/g\nconst openPattern = /\\\\{/g\nconst closePattern = /\\\\}/g\nconst commaPattern = /\\\\,/g\nconst periodPattern = /\\\\\\./g\n\nexport const EXPANSION_MAX = 100_000\n\nfunction numeric(str: string) {\n return !isNaN(str as any) ? parseInt(str, 10) : str.charCodeAt(0)\n}\n\nfunction escapeBraces(str: string) {\n return str\n .replace(slashPattern, escSlash)\n .replace(openPattern, escOpen)\n .replace(closePattern, escClose)\n .replace(commaPattern, escComma)\n .replace(periodPattern, escPeriod)\n}\n\nfunction unescapeBraces(str: string) {\n return str\n .replace(escSlashPattern, '\\\\')\n .replace(escOpenPattern, '{')\n .replace(escClosePattern, '}')\n .replace(escCommaPattern, ',')\n .replace(escPeriodPattern, '.')\n}\n\n/**\n * Basically just str.split(\",\"), but handling cases\n * where we have nested braced sections, which should be\n * treated as individual members, like {a,{b,c},d}\n */\nfunction parseCommaParts(str: string) {\n if (!str) {\n return ['']\n }\n\n const parts: string[] = []\n const m = balanced('{', '}', str)\n\n if (!m) {\n return str.split(',')\n }\n\n const { pre, body, post } = m\n const p = pre.split(',')\n\n p[p.length - 1] += '{' + body + '}'\n const postParts = parseCommaParts(post)\n if (post.length) {\n ;(p[p.length - 1] as string) += postParts.shift()\n p.push.apply(p, postParts)\n }\n\n parts.push.apply(parts, p)\n\n return parts\n}\n\nexport type BraceExpansionOptions = {\n max?: number\n}\n\nexport function expand(str: string, options: BraceExpansionOptions = {}) {\n if (!str) {\n return []\n }\n\n const { max = EXPANSION_MAX } = options\n\n // I don't know why Bash 4.3 does this, but it does.\n // Anything starting with {} will have the first two bytes preserved\n // but *only* at the top level, so {},a}b will not expand to anything,\n // but a{},b}c will be expanded to [a}c,abc].\n // One could argue that this is a bug in Bash, but since the goal of\n // this module is to match Bash's rules, we escape a leading {}\n if (str.slice(0, 2) === '{}') {\n str = '\\\\{\\\\}' + str.slice(2)\n }\n\n return expand_(escapeBraces(str), max, true).map(unescapeBraces)\n}\n\nfunction embrace(str: string) {\n return '{' + str + '}'\n}\n\nfunction isPadded(el: string) {\n return /^-?0\\d/.test(el)\n}\n\nfunction lte(i: number, y: number) {\n return i <= y\n}\n\nfunction gte(i: number, y: number) {\n return i >= y\n}\n\nfunction expand_(str: string, max: number, isTop: boolean): string[] {\n /** @type {string[]} */\n const expansions: string[] = []\n\n const m = balanced('{', '}', str)\n if (!m) return [str]\n\n // no need to expand pre, since it is guaranteed to be free of brace-sets\n const pre = m.pre\n const post: string[] = m.post.length ? expand_(m.post, max, false) : ['']\n\n if (/\\$$/.test(m.pre)) {\n for (let k = 0; k < post.length && k < max; k++) {\n const expansion = pre + '{' + m.body + '}' + post[k]\n expansions.push(expansion)\n }\n } else {\n const isNumericSequence = /^-?\\d+\\.\\.-?\\d+(?:\\.\\.-?\\d+)?$/.test(m.body)\n const isAlphaSequence = /^[a-zA-Z]\\.\\.[a-zA-Z](?:\\.\\.-?\\d+)?$/.test(\n m.body,\n )\n const isSequence = isNumericSequence || isAlphaSequence\n const isOptions = m.body.indexOf(',') >= 0\n if (!isSequence && !isOptions) {\n // {a},b}\n if (m.post.match(/,(?!,).*\\}/)) {\n str = m.pre + '{' + m.body + escClose + m.post\n return expand_(str, max, true)\n }\n return [str]\n }\n\n let n: string[]\n if (isSequence) {\n n = m.body.split(/\\.\\./)\n } else {\n n = parseCommaParts(m.body)\n if (n.length === 1 && n[0] !== undefined) {\n // x{{a,b}}y ==> x{a}y x{b}y\n n = expand_(n[0], max, false).map(embrace)\n //XXX is this necessary? Can't seem to hit it in tests.\n /* c8 ignore start */\n if (n.length === 1) {\n return post.map(p => m.pre + n[0] + p)\n }\n /* c8 ignore stop */\n }\n }\n\n // at this point, n is the parts, and we know it's not a comma set\n // with a single entry.\n let N: string[]\n\n if (isSequence && n[0] !== undefined && n[1] !== undefined) {\n const x = numeric(n[0])\n const y = numeric(n[1])\n const width = Math.max(n[0].length, n[1].length)\n let incr =\n n.length === 3 && n[2] !== undefined ?\n Math.max(Math.abs(numeric(n[2])), 1)\n : 1\n let test = lte\n const reverse = y < x\n if (reverse) {\n incr *= -1\n test = gte\n }\n const pad = n.some(isPadded)\n\n N = []\n\n for (let i = x; test(i, y) && N.length < max; i += incr) {\n let c\n if (isAlphaSequence) {\n c = String.fromCharCode(i)\n if (c === '\\\\') {\n c = ''\n }\n } else {\n c = String(i)\n if (pad) {\n const need = width - c.length\n if (need > 0) {\n const z = new Array(need + 1).join('0')\n if (i < 0) {\n c = '-' + z + c.slice(1)\n } else {\n c = z + c\n }\n }\n }\n }\n N.push(c)\n }\n } else {\n N = []\n\n for (let j = 0; j < n.length; j++) {\n N.push.apply(N, expand_(n[j] as string, max, false))\n }\n }\n\n for (let j = 0; j < N.length; j++) {\n for (let k = 0; k < post.length && expansions.length < max; k++) {\n const expansion = pre + N[j] + post[k]\n if (!isTop || isSequence || expansion) {\n expansions.push(expansion)\n }\n }\n }\n }\n\n return expansions\n}\n"]} \ No newline at end of file diff --git a/node_modules/brace-expansion/dist/esm/index.js b/node_modules/brace-expansion/dist/esm/index.js index 855e22cd..32399e7b 100644 --- a/node_modules/brace-expansion/dist/esm/index.js +++ b/node_modules/brace-expansion/dist/esm/index.js @@ -151,7 +151,7 @@ function expand_(str, max, isTop) { } const pad = n.some(isPadded); N = []; - for (let i = x; test(i, y); i += incr) { + for (let i = x; test(i, y) && N.length < max; i += incr) { let c; if (isAlphaSequence) { c = String.fromCharCode(i); diff --git a/node_modules/brace-expansion/dist/esm/index.js.map b/node_modules/brace-expansion/dist/esm/index.js.map index 1bf0ab5b..63a6a2a9 100644 --- a/node_modules/brace-expansion/dist/esm/index.js.map +++ b/node_modules/brace-expansion/dist/esm/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { balanced } from 'balanced-match'\n\nconst escSlash = '\\0SLASH' + Math.random() + '\\0'\nconst escOpen = '\\0OPEN' + Math.random() + '\\0'\nconst escClose = '\\0CLOSE' + Math.random() + '\\0'\nconst escComma = '\\0COMMA' + Math.random() + '\\0'\nconst escPeriod = '\\0PERIOD' + Math.random() + '\\0'\nconst escSlashPattern = new RegExp(escSlash, 'g')\nconst escOpenPattern = new RegExp(escOpen, 'g')\nconst escClosePattern = new RegExp(escClose, 'g')\nconst escCommaPattern = new RegExp(escComma, 'g')\nconst escPeriodPattern = new RegExp(escPeriod, 'g')\nconst slashPattern = /\\\\\\\\/g\nconst openPattern = /\\\\{/g\nconst closePattern = /\\\\}/g\nconst commaPattern = /\\\\,/g\nconst periodPattern = /\\\\\\./g\n\nexport const EXPANSION_MAX = 100_000\n\nfunction numeric(str: string) {\n return !isNaN(str as any) ? parseInt(str, 10) : str.charCodeAt(0)\n}\n\nfunction escapeBraces(str: string) {\n return str\n .replace(slashPattern, escSlash)\n .replace(openPattern, escOpen)\n .replace(closePattern, escClose)\n .replace(commaPattern, escComma)\n .replace(periodPattern, escPeriod)\n}\n\nfunction unescapeBraces(str: string) {\n return str\n .replace(escSlashPattern, '\\\\')\n .replace(escOpenPattern, '{')\n .replace(escClosePattern, '}')\n .replace(escCommaPattern, ',')\n .replace(escPeriodPattern, '.')\n}\n\n/**\n * Basically just str.split(\",\"), but handling cases\n * where we have nested braced sections, which should be\n * treated as individual members, like {a,{b,c},d}\n */\nfunction parseCommaParts(str: string) {\n if (!str) {\n return ['']\n }\n\n const parts: string[] = []\n const m = balanced('{', '}', str)\n\n if (!m) {\n return str.split(',')\n }\n\n const { pre, body, post } = m\n const p = pre.split(',')\n\n p[p.length - 1] += '{' + body + '}'\n const postParts = parseCommaParts(post)\n if (post.length) {\n ;(p[p.length - 1] as string) += postParts.shift()\n p.push.apply(p, postParts)\n }\n\n parts.push.apply(parts, p)\n\n return parts\n}\n\nexport type BraceExpansionOptions = {\n max?: number\n}\n\nexport function expand(str: string, options: BraceExpansionOptions = {}) {\n if (!str) {\n return []\n }\n\n const { max = EXPANSION_MAX } = options\n\n // I don't know why Bash 4.3 does this, but it does.\n // Anything starting with {} will have the first two bytes preserved\n // but *only* at the top level, so {},a}b will not expand to anything,\n // but a{},b}c will be expanded to [a}c,abc].\n // One could argue that this is a bug in Bash, but since the goal of\n // this module is to match Bash's rules, we escape a leading {}\n if (str.slice(0, 2) === '{}') {\n str = '\\\\{\\\\}' + str.slice(2)\n }\n\n return expand_(escapeBraces(str), max, true).map(unescapeBraces)\n}\n\nfunction embrace(str: string) {\n return '{' + str + '}'\n}\n\nfunction isPadded(el: string) {\n return /^-?0\\d/.test(el)\n}\n\nfunction lte(i: number, y: number) {\n return i <= y\n}\n\nfunction gte(i: number, y: number) {\n return i >= y\n}\n\nfunction expand_(str: string, max: number, isTop: boolean): string[] {\n /** @type {string[]} */\n const expansions: string[] = []\n\n const m = balanced('{', '}', str)\n if (!m) return [str]\n\n // no need to expand pre, since it is guaranteed to be free of brace-sets\n const pre = m.pre\n const post: string[] = m.post.length ? expand_(m.post, max, false) : ['']\n\n if (/\\$$/.test(m.pre)) {\n for (let k = 0; k < post.length && k < max; k++) {\n const expansion = pre + '{' + m.body + '}' + post[k]\n expansions.push(expansion)\n }\n } else {\n const isNumericSequence = /^-?\\d+\\.\\.-?\\d+(?:\\.\\.-?\\d+)?$/.test(m.body)\n const isAlphaSequence = /^[a-zA-Z]\\.\\.[a-zA-Z](?:\\.\\.-?\\d+)?$/.test(\n m.body,\n )\n const isSequence = isNumericSequence || isAlphaSequence\n const isOptions = m.body.indexOf(',') >= 0\n if (!isSequence && !isOptions) {\n // {a},b}\n if (m.post.match(/,(?!,).*\\}/)) {\n str = m.pre + '{' + m.body + escClose + m.post\n return expand_(str, max, true)\n }\n return [str]\n }\n\n let n: string[]\n if (isSequence) {\n n = m.body.split(/\\.\\./)\n } else {\n n = parseCommaParts(m.body)\n if (n.length === 1 && n[0] !== undefined) {\n // x{{a,b}}y ==> x{a}y x{b}y\n n = expand_(n[0], max, false).map(embrace)\n //XXX is this necessary? Can't seem to hit it in tests.\n /* c8 ignore start */\n if (n.length === 1) {\n return post.map(p => m.pre + n[0] + p)\n }\n /* c8 ignore stop */\n }\n }\n\n // at this point, n is the parts, and we know it's not a comma set\n // with a single entry.\n let N: string[]\n\n if (isSequence && n[0] !== undefined && n[1] !== undefined) {\n const x = numeric(n[0])\n const y = numeric(n[1])\n const width = Math.max(n[0].length, n[1].length)\n let incr =\n n.length === 3 && n[2] !== undefined ?\n Math.max(Math.abs(numeric(n[2])), 1)\n : 1\n let test = lte\n const reverse = y < x\n if (reverse) {\n incr *= -1\n test = gte\n }\n const pad = n.some(isPadded)\n\n N = []\n\n for (let i = x; test(i, y); i += incr) {\n let c\n if (isAlphaSequence) {\n c = String.fromCharCode(i)\n if (c === '\\\\') {\n c = ''\n }\n } else {\n c = String(i)\n if (pad) {\n const need = width - c.length\n if (need > 0) {\n const z = new Array(need + 1).join('0')\n if (i < 0) {\n c = '-' + z + c.slice(1)\n } else {\n c = z + c\n }\n }\n }\n }\n N.push(c)\n }\n } else {\n N = []\n\n for (let j = 0; j < n.length; j++) {\n N.push.apply(N, expand_(n[j] as string, max, false))\n }\n }\n\n for (let j = 0; j < N.length; j++) {\n for (let k = 0; k < post.length && expansions.length < max; k++) {\n const expansion = pre + N[j] + post[k]\n if (!isTop || isSequence || expansion) {\n expansions.push(expansion)\n }\n }\n }\n }\n\n return expansions\n}\n"]} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { balanced } from 'balanced-match'\n\nconst escSlash = '\\0SLASH' + Math.random() + '\\0'\nconst escOpen = '\\0OPEN' + Math.random() + '\\0'\nconst escClose = '\\0CLOSE' + Math.random() + '\\0'\nconst escComma = '\\0COMMA' + Math.random() + '\\0'\nconst escPeriod = '\\0PERIOD' + Math.random() + '\\0'\nconst escSlashPattern = new RegExp(escSlash, 'g')\nconst escOpenPattern = new RegExp(escOpen, 'g')\nconst escClosePattern = new RegExp(escClose, 'g')\nconst escCommaPattern = new RegExp(escComma, 'g')\nconst escPeriodPattern = new RegExp(escPeriod, 'g')\nconst slashPattern = /\\\\\\\\/g\nconst openPattern = /\\\\{/g\nconst closePattern = /\\\\}/g\nconst commaPattern = /\\\\,/g\nconst periodPattern = /\\\\\\./g\n\nexport const EXPANSION_MAX = 100_000\n\nfunction numeric(str: string) {\n return !isNaN(str as any) ? parseInt(str, 10) : str.charCodeAt(0)\n}\n\nfunction escapeBraces(str: string) {\n return str\n .replace(slashPattern, escSlash)\n .replace(openPattern, escOpen)\n .replace(closePattern, escClose)\n .replace(commaPattern, escComma)\n .replace(periodPattern, escPeriod)\n}\n\nfunction unescapeBraces(str: string) {\n return str\n .replace(escSlashPattern, '\\\\')\n .replace(escOpenPattern, '{')\n .replace(escClosePattern, '}')\n .replace(escCommaPattern, ',')\n .replace(escPeriodPattern, '.')\n}\n\n/**\n * Basically just str.split(\",\"), but handling cases\n * where we have nested braced sections, which should be\n * treated as individual members, like {a,{b,c},d}\n */\nfunction parseCommaParts(str: string) {\n if (!str) {\n return ['']\n }\n\n const parts: string[] = []\n const m = balanced('{', '}', str)\n\n if (!m) {\n return str.split(',')\n }\n\n const { pre, body, post } = m\n const p = pre.split(',')\n\n p[p.length - 1] += '{' + body + '}'\n const postParts = parseCommaParts(post)\n if (post.length) {\n ;(p[p.length - 1] as string) += postParts.shift()\n p.push.apply(p, postParts)\n }\n\n parts.push.apply(parts, p)\n\n return parts\n}\n\nexport type BraceExpansionOptions = {\n max?: number\n}\n\nexport function expand(str: string, options: BraceExpansionOptions = {}) {\n if (!str) {\n return []\n }\n\n const { max = EXPANSION_MAX } = options\n\n // I don't know why Bash 4.3 does this, but it does.\n // Anything starting with {} will have the first two bytes preserved\n // but *only* at the top level, so {},a}b will not expand to anything,\n // but a{},b}c will be expanded to [a}c,abc].\n // One could argue that this is a bug in Bash, but since the goal of\n // this module is to match Bash's rules, we escape a leading {}\n if (str.slice(0, 2) === '{}') {\n str = '\\\\{\\\\}' + str.slice(2)\n }\n\n return expand_(escapeBraces(str), max, true).map(unescapeBraces)\n}\n\nfunction embrace(str: string) {\n return '{' + str + '}'\n}\n\nfunction isPadded(el: string) {\n return /^-?0\\d/.test(el)\n}\n\nfunction lte(i: number, y: number) {\n return i <= y\n}\n\nfunction gte(i: number, y: number) {\n return i >= y\n}\n\nfunction expand_(str: string, max: number, isTop: boolean): string[] {\n /** @type {string[]} */\n const expansions: string[] = []\n\n const m = balanced('{', '}', str)\n if (!m) return [str]\n\n // no need to expand pre, since it is guaranteed to be free of brace-sets\n const pre = m.pre\n const post: string[] = m.post.length ? expand_(m.post, max, false) : ['']\n\n if (/\\$$/.test(m.pre)) {\n for (let k = 0; k < post.length && k < max; k++) {\n const expansion = pre + '{' + m.body + '}' + post[k]\n expansions.push(expansion)\n }\n } else {\n const isNumericSequence = /^-?\\d+\\.\\.-?\\d+(?:\\.\\.-?\\d+)?$/.test(m.body)\n const isAlphaSequence = /^[a-zA-Z]\\.\\.[a-zA-Z](?:\\.\\.-?\\d+)?$/.test(\n m.body,\n )\n const isSequence = isNumericSequence || isAlphaSequence\n const isOptions = m.body.indexOf(',') >= 0\n if (!isSequence && !isOptions) {\n // {a},b}\n if (m.post.match(/,(?!,).*\\}/)) {\n str = m.pre + '{' + m.body + escClose + m.post\n return expand_(str, max, true)\n }\n return [str]\n }\n\n let n: string[]\n if (isSequence) {\n n = m.body.split(/\\.\\./)\n } else {\n n = parseCommaParts(m.body)\n if (n.length === 1 && n[0] !== undefined) {\n // x{{a,b}}y ==> x{a}y x{b}y\n n = expand_(n[0], max, false).map(embrace)\n //XXX is this necessary? Can't seem to hit it in tests.\n /* c8 ignore start */\n if (n.length === 1) {\n return post.map(p => m.pre + n[0] + p)\n }\n /* c8 ignore stop */\n }\n }\n\n // at this point, n is the parts, and we know it's not a comma set\n // with a single entry.\n let N: string[]\n\n if (isSequence && n[0] !== undefined && n[1] !== undefined) {\n const x = numeric(n[0])\n const y = numeric(n[1])\n const width = Math.max(n[0].length, n[1].length)\n let incr =\n n.length === 3 && n[2] !== undefined ?\n Math.max(Math.abs(numeric(n[2])), 1)\n : 1\n let test = lte\n const reverse = y < x\n if (reverse) {\n incr *= -1\n test = gte\n }\n const pad = n.some(isPadded)\n\n N = []\n\n for (let i = x; test(i, y) && N.length < max; i += incr) {\n let c\n if (isAlphaSequence) {\n c = String.fromCharCode(i)\n if (c === '\\\\') {\n c = ''\n }\n } else {\n c = String(i)\n if (pad) {\n const need = width - c.length\n if (need > 0) {\n const z = new Array(need + 1).join('0')\n if (i < 0) {\n c = '-' + z + c.slice(1)\n } else {\n c = z + c\n }\n }\n }\n }\n N.push(c)\n }\n } else {\n N = []\n\n for (let j = 0; j < n.length; j++) {\n N.push.apply(N, expand_(n[j] as string, max, false))\n }\n }\n\n for (let j = 0; j < N.length; j++) {\n for (let k = 0; k < post.length && expansions.length < max; k++) {\n const expansion = pre + N[j] + post[k]\n if (!isTop || isSequence || expansion) {\n expansions.push(expansion)\n }\n }\n }\n }\n\n return expansions\n}\n"]} \ No newline at end of file diff --git a/node_modules/brace-expansion/package.json b/node_modules/brace-expansion/package.json index 83a82896..81524809 100644 --- a/node_modules/brace-expansion/package.json +++ b/node_modules/brace-expansion/package.json @@ -1,7 +1,7 @@ { "name": "brace-expansion", "description": "Brace expansion as known from sh/bash", - "version": "5.0.5", + "version": "5.0.6", "files": [ "dist" ], diff --git a/node_modules/nodemailer/CHANGELOG.md b/node_modules/nodemailer/CHANGELOG.md index d8ebaed8..e47bfd64 100644 --- a/node_modules/nodemailer/CHANGELOG.md +++ b/node_modules/nodemailer/CHANGELOG.md @@ -1,5 +1,52 @@ # CHANGELOG +## [9.0.0](https://github.com/nodemailer/nodemailer/compare/v8.0.11...v9.0.0) (2026-06-14) + + +### ⚠ BREAKING CHANGES + +* HTTPS requests made while fetching remote content (attachment href/path URLs, OAuth2 token endpoints, HTTP/HTTPS proxy CONNECT) now validate the server's TLS certificate by default. Requests to hosts with self-signed, expired, or hostname-mismatched certificates that previously succeeded will now fail. Opt back out per request with tls.rejectUnauthorized=false (transport options, or a per-attachment `tls` option). + +### Bug Fixes + +* replace deprecated url.parse with a WHATWG URL wrapper ([0c080fb](https://github.com/nodemailer/nodemailer/commit/0c080fbf3278926f013a5c2ad06f5f6f0e18f5ed)) +* validate TLS certificates by default when fetching remote content ([6a947ac](https://github.com/nodemailer/nodemailer/commit/6a947ac7114a16da1e6a50d9a6f4e17026ce145d)) + +## [8.0.11](https://github.com/nodemailer/nodemailer/compare/v8.0.10...v8.0.11) (2026-06-10) + + +### Bug Fixes + +* apply the transport-level newline option in stream and sendmail transports ([cb4f904](https://github.com/nodemailer/nodemailer/commit/cb4f904a53d2c2feeaf327203c92378d46304398)) +* include icalEvent path/href content in the application/ics attachment ([b801c48](https://github.com/nodemailer/nodemailer/commit/b801c48fab8e9b71bc7e0ea1fb32ce6b34675b15)) +* parse Ethereal response props without polynomial regex backtracking ([067aebe](https://github.com/nodemailer/nodemailer/commit/067aebec83b8cbe7682905e89b30ab19d260b503)) +* resolve oauth2_provision_cb at send time for non-pooled SMTP transports ([203c8ec](https://github.com/nodemailer/nodemailer/commit/203c8ecf97594ac2e69919b0f3ba966c0f86750e)) +* return the promise from every resolveContent branch ([07ffe8c](https://github.com/nodemailer/nodemailer/commit/07ffe8cfd97f0486b8c7b541f398922ddab47882)) +* strip the url scheme from List-ID header values ([77e5885](https://github.com/nodemailer/nodemailer/commit/77e5885cfa0c6723ea7749c1ee74b1c11aeb78bd)) +* tag AWS SES transport errors with the ESES code ([efa647a](https://github.com/nodemailer/nodemailer/commit/efa647a125dd698413a7cf6813b8e36881a06f91)) + +## [8.0.10](https://github.com/nodemailer/nodemailer/compare/v8.0.9...v8.0.10) (2026-05-29) + + +### Bug Fixes + +* fall back to lower-severity handler when custom logger lacks a level method ([6d849df](https://github.com/nodemailer/nodemailer/commit/6d849df59a56184b48844ed10b5fb7b8e9f74634)) + +## [8.0.9](https://github.com/nodemailer/nodemailer/compare/v8.0.8...v8.0.9) (2026-05-26) + + +### Bug Fixes + +* two pending security advisories (jsonTransport access bypass, List-* CRLF injection) ([#1820](https://github.com/nodemailer/nodemailer/issues/1820)) ([5f69497](https://github.com/nodemailer/nodemailer/commit/5f694977da2e0e13dc947037566e8e689a01217e)) + +## [8.0.8](https://github.com/nodemailer/nodemailer/compare/v8.0.7...v8.0.8) (2026-05-23) + + +### Bug Fixes + +* enforce strict TLS for OAuth2 and Ethereal credential requests ([#1818](https://github.com/nodemailer/nodemailer/issues/1818)) ([833d6e5](https://github.com/nodemailer/nodemailer/commit/833d6e58c8b717962bbb1b23e16923cd267c3bc9)) +* four listener/stream leaks in SMTP transport, connection, pool ([#1817](https://github.com/nodemailer/nodemailer/issues/1817)) ([850bb91](https://github.com/nodemailer/nodemailer/commit/850bb91bff7707ed498c1424df01c4e5b30ea14b)) + ## [8.0.7](https://github.com/nodemailer/nodemailer/compare/v8.0.6...v8.0.7) (2026-04-27) diff --git a/node_modules/nodemailer/CLAUDE.md b/node_modules/nodemailer/CLAUDE.md index e31b392c..c0f6d6cd 100644 --- a/node_modules/nodemailer/CLAUDE.md +++ b/node_modules/nodemailer/CLAUDE.md @@ -50,6 +50,7 @@ Conventional Commit prefixes used in this repo: `fix:`, `feat:`, `chore:`, `docs ## Security This is a widely-deployed library — security-sensitive changes get extra scrutiny: + - SMTP command injection: any user-controllable value that flows into a written SMTP command (envelope addresses, sizes, the `name`/EHLO option, headers) must be CRLF-stripped or rejected at the boundary. Sanitize at the assignment, not at every call site. - Server reply parsing in `lib/smtp-connection/index.js` uses a `'binary'` byte-container intermediate to reassemble multi-byte UTF-8 across socket chunks; the actual decode happens at line boundaries via `decodeServerResponse`. Don't change the chunk-buffering encoding without understanding why. - Reference the GHSA ID in commit messages for advisories. diff --git a/node_modules/nodemailer/SECURITY.md b/node_modules/nodemailer/SECURITY.md new file mode 100644 index 00000000..2243c06f --- /dev/null +++ b/node_modules/nodemailer/SECURITY.md @@ -0,0 +1,65 @@ +# Security Policy + +Nodemailer is a widely deployed, zero-dependency e-mail library. We take security +reports seriously and aim to respond quickly. + +## Supported Versions + +Security fixes are released only against the latest major version. We do not +backport patches to older majors — upgrading to the current release line is the +supported way to receive security updates. + +| Version | Supported | +| ------- | ------------------ | +| 8.x | :white_check_mark: | +| < 8.0 | :x: | + +If you are on an older major, please upgrade. See the migration notes at + before updating. + +## Reporting a Vulnerability + +**Please do not report security vulnerabilities through public GitHub issues, +pull requests, or discussions.** + +Report privately through one of the following channels: + +1. **GitHub Security Advisories (preferred).** Open a private report at + . This keeps + the discussion private until a fix is published and lets us credit you. +2. **Email.** Send details to **andris@reinman.eu** (the contact listed in + [`SECURITY.txt`](SECURITY.txt)). Encrypt sensitive details if possible. + +When reporting, please include as much of the following as you can: + +- The affected version(s) and environment (Node.js version, OS). +- The component involved (e.g. SMTP connection, address parsing, MIME/header + generation, DKIM). +- A clear description of the issue and its impact (e.g. header/SMTP command + injection, information disclosure, DoS). +- A minimal proof of concept or reproduction steps. +- Any suggested remediation, if you have one. + +Nodemailer is maintained by a single person, so there is no guaranteed response +time — sometimes reports are handled within hours, sometimes they take longer. +Accepted issues are fixed in a new release and coordinated through a GitHub +Security Advisory, and reporters who wish to be named are credited. + +## CVEs + +We track and disclose vulnerabilities through GitHub Security Advisories. We do +not request or manage CVE identifiers ourselves. If you need a CVE assigned for a +reported issue, please request one yourself — for example, through GitHub's own +CVE request flow on the published advisory, or another CNA. + +## Scope + +In scope: the `nodemailer` package source in this repository — message and MIME +generation, SMTP/LMTP client behaviour, address parsing, header handling, DKIM +signing, and the bundled transports. + +Out of scope: vulnerabilities in your own application code, misconfiguration of +your mail server or credentials, social-engineering reports, and issues in +third-party services Nodemailer connects to. + +Thank you for helping keep Nodemailer and its users safe. diff --git a/node_modules/nodemailer/lib/fetch/cookies.js b/node_modules/nodemailer/lib/fetch/cookies.js index bd1c328e..2b5e452a 100644 --- a/node_modules/nodemailer/lib/fetch/cookies.js +++ b/node_modules/nodemailer/lib/fetch/cookies.js @@ -2,7 +2,7 @@ // module to handle cookies -const urllib = require('url'); +const urllib = require('../shared/url'); const SESSION_TIMEOUT = 1800; // 30 min diff --git a/node_modules/nodemailer/lib/fetch/index.js b/node_modules/nodemailer/lib/fetch/index.js index 6c838365..9cf4d2fd 100644 --- a/node_modules/nodemailer/lib/fetch/index.js +++ b/node_modules/nodemailer/lib/fetch/index.js @@ -2,7 +2,7 @@ const http = require('http'); const https = require('https'); -const urllib = require('url'); +const urllib = require('../shared/url'); const zlib = require('zlib'); const { PassThrough } = require('stream'); const Cookies = require('./cookies'); @@ -123,7 +123,10 @@ function nmfetch(url, options) { path: parsed.path, port: parsed.port ? parsed.port : parsed.protocol === 'https:' ? 443 : 80, headers, - rejectUnauthorized: false, + // Validate TLS certificates by default. Callers that genuinely need to + // reach a self-signed/internal host opt out explicitly with + // options.tls = { rejectUnauthorized: false }. + rejectUnauthorized: true, agent: false }; @@ -212,7 +215,27 @@ function nmfetch(url, options) { // redirect does not include POST body options.method = 'GET'; options.body = false; - return nmfetch(urllib.resolve(url, res.headers.location), options); + + const redirectUrl = urllib.resolve(url, res.headers.location); + const redirectParsed = urllib.parse(redirectUrl); + + // Do not forward credentials when the redirect leaves the original + // security context: a different host, or a downgrade from https to + // http (which would otherwise put them on the wire in cleartext). + // Strip sensitive request headers so an attacker who controls the + // redirect target cannot harvest them. + const crossHost = redirectParsed.hostname !== parsed.hostname; + const downgrade = parsed.protocol === 'https:' && redirectParsed.protocol === 'http:'; + if (options.headers && (crossHost || downgrade)) { + const sensitive = ['authorization', 'cookie', 'proxy-authorization']; + Object.keys(options.headers).forEach(key => { + if (sensitive.includes(key.toLowerCase())) { + delete options.headers[key]; + } + }); + } + + return nmfetch(redirectUrl, options); } fetchRes.statusCode = res.statusCode; diff --git a/node_modules/nodemailer/lib/mail-composer/index.js b/node_modules/nodemailer/lib/mail-composer/index.js index ccc93699..007c7e5a 100644 --- a/node_modules/nodemailer/lib/mail-composer/index.js +++ b/node_modules/nodemailer/lib/mail-composer/index.js @@ -83,7 +83,7 @@ class MailComposer { * @returns {Object} An object of arrays (`related` and `attached`) */ getAttachments(findRelated) { - let icalEvent, eventObject; + let eventObject; const attachments = [].concat(this.mail.attachments || []).map((attachment, i) => { if (/^data:/i.test(attachment.path || attachment.href)) { attachment = this._processDataUrl(attachment); @@ -142,7 +142,8 @@ class MailComposer { } else if (attachment.href) { data.content = { href: attachment.href, - httpHeaders: attachment.httpHeaders + httpHeaders: attachment.httpHeaders, + tls: attachment.tls }; } else { data.content = attachment.content || ''; @@ -160,18 +161,7 @@ class MailComposer { }); if (this.mail.icalEvent) { - if ( - typeof this.mail.icalEvent === 'object' && - (this.mail.icalEvent.content || this.mail.icalEvent.path || this.mail.icalEvent.href || this.mail.icalEvent.raw) - ) { - icalEvent = this.mail.icalEvent; - } else { - icalEvent = { - content: this.mail.icalEvent - }; - } - - eventObject = Object.assign({}, icalEvent); + eventObject = Object.assign({}, this._getIcalEvent()); eventObject.contentType = 'application/ics'; if (!eventObject.headers) { @@ -195,6 +185,67 @@ class MailComposer { }; } + /** + * Returns the icalEvent value with `path`/`href`/data uri input normalized into + * a `content` entry, the same way as for regular attachments. The same event is + * included twice (as a text/calendar alternative and as an application/ics + * attachment), so the shared content object is marked to be resolved just once + * and the buffered result is reused by the second node. + * + * @returns {Object} Normalized icalEvent data + */ + _getIcalEvent() { + if (!this._icalEvent) { + let icalEvent; + if ( + typeof this.mail.icalEvent === 'object' && + (this.mail.icalEvent.content || this.mail.icalEvent.path || this.mail.icalEvent.href || this.mail.icalEvent.raw) + ) { + icalEvent = Object.assign({}, this.mail.icalEvent); + } else { + icalEvent = { + content: this.mail.icalEvent + }; + } + + if (/^data:/i.test(icalEvent.path || icalEvent.href)) { + icalEvent = this._processDataUrl(icalEvent); + } + + if (/^https?:\/\//i.test(icalEvent.path)) { + icalEvent.href = icalEvent.path; + icalEvent.path = undefined; + } + + if (!icalEvent.raw) { + // map file path and URL values into `content`, otherwise the content + // nodes would render an empty body + if (icalEvent.path) { + icalEvent.content = { + path: icalEvent.path + }; + icalEvent.path = undefined; + } else if (icalEvent.href) { + icalEvent.content = { + href: icalEvent.href, + httpHeaders: icalEvent.httpHeaders + }; + icalEvent.href = undefined; + } + } + + if (icalEvent.content && typeof icalEvent.content === 'object') { + // we are going to have the same attachment twice, so mark this to be + // resolved just once + icalEvent.content._resolve = true; + } + + this._icalEvent = icalEvent; + } + + return this._icalEvent; + } + /** * List alternatives. Resulting objects can be used as input for MimeNode nodes * @@ -202,7 +253,7 @@ class MailComposer { */ getAlternatives() { const alternatives = []; - let text, html, watchHtml, amp, icalEvent, eventObject; + let text, html, watchHtml, amp, eventObject; if (this.mail.text) { if ( @@ -248,24 +299,7 @@ class MailComposer { // NB! when including attachments with a calendar alternative you might end up in a blank screen on some clients if (this.mail.icalEvent) { - if ( - typeof this.mail.icalEvent === 'object' && - (this.mail.icalEvent.content || this.mail.icalEvent.path || this.mail.icalEvent.href || this.mail.icalEvent.raw) - ) { - icalEvent = this.mail.icalEvent; - } else { - icalEvent = { - content: this.mail.icalEvent - }; - } - - eventObject = Object.assign({}, icalEvent); - - if (eventObject.content && typeof eventObject.content === 'object') { - // we are going to have the same attachment twice, so mark this to be - // resolved just once - eventObject.content._resolve = true; - } + eventObject = Object.assign({}, this._getIcalEvent()); eventObject.filename = false; eventObject.contentType = diff --git a/node_modules/nodemailer/lib/mailer/index.js b/node_modules/nodemailer/lib/mailer/index.js index 971438ba..cb40f7b5 100644 --- a/node_modules/nodemailer/lib/mailer/index.js +++ b/node_modules/nodemailer/lib/mailer/index.js @@ -8,7 +8,7 @@ const DKIM = require('../dkim'); const httpProxyClient = require('../smtp-connection/http-proxy-client'); const errors = require('../errors'); const util = require('util'); -const urllib = require('url'); +const urllib = require('../shared/url'); const packageData = require('../../package.json'); const MailMessage = require('./mail-message'); const net = require('net'); @@ -324,7 +324,7 @@ class Mail extends EventEmitter { // Connect using a HTTP CONNECT method case 'http': case 'https': - httpProxyClient(proxy.href, options.port, options.host, (err, socket) => { + httpProxyClient(proxy.href, options.port, options.host, this.options.tls || {}, (err, socket) => { if (err) { return callback(err); } @@ -407,31 +407,39 @@ class Mail extends EventEmitter { if ((!this.options.attachDataUrls && !mail.data.attachDataUrls) || !mail.data.html) { return callback(); } - mail.resolveContent(mail.data, 'html', (err, html) => { - if (err) { - return callback(err); + mail.resolveContent( + mail.data, + 'html', + { disableFileAccess: mail.data.disableFileAccess, disableUrlAccess: mail.data.disableUrlAccess }, + (err, html) => { + if (err) { + return callback(err); + } + let cidCounter = 0; + html = (html || '') + .toString() + .replace( + /(]{0,1024} src\s{0,20}=[\s"']{0,20})(data:([^;]+);[^"'>\s]+)/gi, + (match, prefix, dataUri, mimeType) => { + const cid = crypto.randomBytes(10).toString('hex') + '@localhost'; + if (!mail.data.attachments) { + mail.data.attachments = []; + } + if (!Array.isArray(mail.data.attachments)) { + mail.data.attachments = [].concat(mail.data.attachments || []); + } + mail.data.attachments.push({ + path: dataUri, + cid, + filename: 'image-' + ++cidCounter + '.' + mimeTypes.detectExtension(mimeType) + }); + return prefix + 'cid:' + cid; + } + ); + mail.data.html = html; + callback(); } - let cidCounter = 0; - html = (html || '') - .toString() - .replace(/(]{0,1024} src\s{0,20}=[\s"']{0,20})(data:([^;]+);[^"'>\s]+)/gi, (match, prefix, dataUri, mimeType) => { - const cid = crypto.randomBytes(10).toString('hex') + '@localhost'; - if (!mail.data.attachments) { - mail.data.attachments = []; - } - if (!Array.isArray(mail.data.attachments)) { - mail.data.attachments = [].concat(mail.data.attachments || []); - } - mail.data.attachments.push({ - path: dataUri, - cid, - filename: 'image-' + ++cidCounter + '.' + mimeTypes.detectExtension(mimeType) - }); - return prefix + 'cid:' + cid; - }); - mail.data.html = html; - callback(); - }); + ); } set(key, value) { diff --git a/node_modules/nodemailer/lib/mailer/mail-message.js b/node_modules/nodemailer/lib/mailer/mail-message.js index a88df32c..83544d9d 100644 --- a/node_modules/nodemailer/lib/mailer/mail-message.js +++ b/node_modules/nodemailer/lib/mailer/mail-message.js @@ -111,25 +111,29 @@ class MailMessage { if (!args[0] || !args[0][args[1]]) { return resolveNext(); } - shared.resolveContent(...args, (err, value) => { - if (err) { - return callback(err); - } + shared.resolveContent( + ...args, + { disableFileAccess: this.data.disableFileAccess, disableUrlAccess: this.data.disableUrlAccess }, + (err, value) => { + if (err) { + return callback(err); + } - const node = { - content: value - }; - if (args[0][args[1]] && typeof args[0][args[1]] === 'object' && !Buffer.isBuffer(args[0][args[1]])) { - Object.keys(args[0][args[1]]).forEach(key => { - if (!(key in node) && !['content', 'path', 'href', 'raw'].includes(key)) { - node[key] = args[0][args[1]][key]; - } - }); - } + const node = { + content: value + }; + if (args[0][args[1]] && typeof args[0][args[1]] === 'object' && !Buffer.isBuffer(args[0][args[1]])) { + Object.keys(args[0][args[1]]).forEach(key => { + if (!(key in node) && !['content', 'path', 'href', 'raw'].includes(key)) { + node[key] = args[0][args[1]][key]; + } + }); + } - args[0][args[1]] = node; - resolveNext(); - }); + args[0][args[1]] = node; + resolveNext(); + } + ); }; setImmediate(() => resolveNext()); @@ -269,18 +273,24 @@ class MailMessage { if (value && value.url) { if (key.toLowerCase().trim() === 'id') { // List-ID: "comment" - let comment = value.comment || ''; + // strip CR/LF so a comment can't inject extra header lines + let comment = (value.comment || '').toString().replace(/\r?\n|\r/g, ' '); if (mimeFuncs.isPlainText(comment)) { comment = '"' + comment + '"'; } else { comment = mimeFuncs.encodeWord(comment); } - return (value.comment ? comment + ' ' : '') + this._formatListUrl(value.url).replace(/^<[^:]+\/{,2}/, ''); + // List-ID expects a bare domain-like identifier, so strip the + // scheme prefix that _formatListUrl adds or passes through + return ( + (value.comment ? comment + ' ' : '') + this._formatListUrl(value.url).replace(/^<[^:]+:\/{0,2}/, '<') + ); } // List-*: (comment) - let comment = value.comment || ''; + // strip CR/LF so a comment can't inject extra header lines + let comment = (value.comment || '').toString().replace(/\r?\n|\r/g, ' '); if (!mimeFuncs.isPlainText(comment)) { comment = mimeFuncs.encodeWord(comment); } diff --git a/node_modules/nodemailer/lib/mime-funcs/mime-types.js b/node_modules/nodemailer/lib/mime-funcs/mime-types.js index 126db62e..11096430 100644 --- a/node_modules/nodemailer/lib/mime-funcs/mime-types.js +++ b/node_modules/nodemailer/lib/mime-funcs/mime-types.js @@ -2087,7 +2087,7 @@ module.exports = { if (!mimeType) { return defaultExtension; } - const parts = (mimeType || '').toLowerCase().trim().split('/'); + const parts = mimeType.toLowerCase().trim().split('/'); const rootType = parts.shift().trim(); const subType = parts.join('/').trim(); diff --git a/node_modules/nodemailer/lib/mime-node/index.js b/node_modules/nodemailer/lib/mime-node/index.js index 586b268a..f000cb62 100644 --- a/node_modules/nodemailer/lib/mime-node/index.js +++ b/node_modules/nodemailer/lib/mime-node/index.js @@ -1006,7 +1006,7 @@ class MimeNode { return contentStream; } // fetch URL - return nmfetch(content.href, { headers: content.httpHeaders }); + return nmfetch(content.href, { headers: content.httpHeaders, tls: content.tls }); } // pass string or buffer content as a stream diff --git a/node_modules/nodemailer/lib/nodemailer.js b/node_modules/nodemailer/lib/nodemailer.js index 379a7506..140803b3 100644 --- a/node_modules/nodemailer/lib/nodemailer.js +++ b/node_modules/nodemailer/lib/nodemailer.js @@ -95,12 +95,22 @@ module.exports.createTestAccount = function (apiUrl, callback) { requestHeaders.Authorization = 'Bearer ' + ETHEREAL_API_KEY; } - const req = nmfetch(apiUrl + '/user', { + const fetchOptions = { contentType: 'application/json', method: 'POST', headers: requestHeaders, body: Buffer.from(JSON.stringify(requestBody)) - }); + }; + + // Credential-bearing request to the Ethereal API. lib/fetch already + // validates certs by default; pin rejectUnauthorized:true here so this + // call stays strict regardless of any future default change and is never + // relaxed for a real-cert endpoint. + if (/^https:/i.test(apiUrl)) { + fetchOptions.tls = { rejectUnauthorized: true }; + } + + const req = nmfetch(apiUrl + '/user', fetchOptions); req.on('readable', () => { let chunk; @@ -137,11 +147,20 @@ module.exports.getTestMessageUrl = function (info) { } const infoProps = new Map(); - info.response.replace(/\[([^\]]+)\]$/, (m, props) => { - props.replace(/\b([A-Z0-9]+)=([^\s]+)/g, (m, key, value) => { - infoProps.set(key, value); - }); - }); + + // Extract the trailing "[...]" part of the response (no "]" allowed inside) + // with linear string scanning; the equivalent regex /\[([^\]]+)\]$/ was + // flagged for polynomial backtracking on adversarial server responses + const response = info.response.toString(); + if (response.length > 2 && response.charAt(response.length - 1) === ']') { + const open = response.indexOf('[', response.lastIndexOf(']', response.length - 2) + 1); + if (open >= 0 && open < response.length - 2) { + const props = response.substring(open + 1, response.length - 1); + props.replace(/\b([A-Z0-9]+)=([^\s]+)/g, (m, key, value) => { + infoProps.set(key, value); + }); + } + } if (infoProps.has('STATUS') && infoProps.has('MSGID')) { return (testAccount.web || ETHEREAL_WEB) + '/message/' + infoProps.get('MSGID'); diff --git a/node_modules/nodemailer/lib/sendmail-transport/index.js b/node_modules/nodemailer/lib/sendmail-transport/index.js index 8be9db5f..b89d5dd2 100644 --- a/node_modules/nodemailer/lib/sendmail-transport/index.js +++ b/node_modules/nodemailer/lib/sendmail-transport/index.js @@ -4,6 +4,8 @@ const { spawn } = require('child_process'); const packageData = require('../../package.json'); const shared = require('../shared'); const errors = require('../errors'); +const LeWindows = require('../mime-node/le-windows'); +const LeUnix = require('../mime-node/le-unix'); /** * Generates a Transport object for Sendmail @@ -46,6 +48,8 @@ class SendmailTransport { this.args = options.args; } } + + this.winbreak = ['win', 'windows', 'dos', '\r\n'].includes((options.newline || '').toString().toLowerCase()); } /** @@ -178,7 +182,15 @@ class SendmailTransport { ); const sourceStream = mail.message.createReadStream(); - sourceStream.once('error', err => { + let stream = sourceStream; + if (this.options.newline) { + // apply the transport-level line ending transform; the message-level + // `newline` option is handled by MimeNode in createReadStream() + stream = sourceStream.pipe(this.winbreak ? new LeWindows() : new LeUnix()); + sourceStream.once('error', err => stream.emit('error', err)); + } + + stream.once('error', err => { this.logger.error( { err, @@ -193,7 +205,7 @@ class SendmailTransport { callback(err); }); - sourceStream.pipe(sendmail.stdin); + stream.pipe(sendmail.stdin); } else { const err = new Error('sendmail was not found'); err.code = errors.ESENDMAIL; diff --git a/node_modules/nodemailer/lib/ses-transport/index.js b/node_modules/nodemailer/lib/ses-transport/index.js index 3f966aa2..4b5892cb 100644 --- a/node_modules/nodemailer/lib/ses-transport/index.js +++ b/node_modules/nodemailer/lib/ses-transport/index.js @@ -3,9 +3,22 @@ const EventEmitter = require('events'); const packageData = require('../../package.json'); const shared = require('../shared'); +const errors = require('../errors'); const LeWindows = require('../mime-node/le-windows'); const MimeNode = require('../mime-node'); +/** + * Tags AWS SDK rejections that carry no `code` property (SDK v3 errors only + * have a `name`) with the generic SES transport error code, keeping the + * original error object intact + */ +function tagSesError(err) { + if (err && typeof err === 'object' && !err.code) { + err.code = errors.ESES; + } + return err; +} + /** * Generates a Transport object for AWS SES * @@ -157,6 +170,7 @@ class SESTransport extends EventEmitter { }); }) .catch(err => { + tagSesError(err); this.logger.error( { err, @@ -188,7 +202,7 @@ class SESTransport extends EventEmitter { const cb = err => { if (err && !['InvalidParameterValue', 'MessageRejected'].includes(err.code || err.Code || err.name)) { - return callback(err); + return callback(tagSesError(err)); } return callback(null, true); }; @@ -205,15 +219,13 @@ class SESTransport extends EventEmitter { } }; - this.getRegion((err, region) => { - if (err || !region) { - region = 'us-east-1'; - } - + // the region value is not used for anything when verifying, but the lookup + // exercises the client configuration the same way as send() does + this.getRegion(() => { const command = new this.ses.SendEmailCommand(sesMessage); const sendPromise = this.ses.sesClient.send(command); - sendPromise.then(data => cb(null, data)).catch(err => cb(err)); + sendPromise.then(() => cb(null)).catch(err => cb(err)); }); return promise; diff --git a/node_modules/nodemailer/lib/shared/index.js b/node_modules/nodemailer/lib/shared/index.js index 2f923c2b..8918fcf4 100644 --- a/node_modules/nodemailer/lib/shared/index.js +++ b/node_modules/nodemailer/lib/shared/index.js @@ -2,10 +2,11 @@ 'use strict'; -const urllib = require('url'); +const urllib = require('./url'); const util = require('util'); const fs = require('fs'); const nmfetch = require('../fetch'); +const errors = require('../errors'); const dns = require('dns'); const net = require('net'); const os = require('os'); @@ -366,7 +367,16 @@ module.exports._logFunc = (logger, level, defaults, data, message, ...args) => { const entry = Object.assign({}, defaults || {}, data || {}); delete entry.level; - logger[level](entry, message, ...args); + let logLevel = level; + if (typeof logger[logLevel] !== 'function') { + // Provided logger does not implement this level. Fall back to a + // lower-severity handler instead of throwing. + logLevel = ['info', 'debug', 'log', 'trace', 'warn', 'error'].find(name => typeof logger[name] === 'function'); + } + + if (logLevel) { + logger[logLevel](entry, message, ...args); + } }; /** @@ -501,9 +511,17 @@ module.exports.parseDataURI = uri => { * * @param {Object} data An object or an Array you want to resolve an element for * @param {String|Number} key Property name or an Array index + * @param {Object} [options] Optional access policy: { disableFileAccess, disableUrlAccess } * @param {Function} callback Callback function with (err, value) */ -module.exports.resolveContent = (data, key, callback) => { +module.exports.resolveContent = (data, key, options, callback) => { + // options is optional; support the legacy resolveContent(data, key, callback) signature + if (!callback && typeof options === 'function') { + callback = options; + options = false; + } + options = options || {}; + let promise; if (!callback) { @@ -512,6 +530,12 @@ module.exports.resolveContent = (data, key, callback) => { }); } + resolveContentValue(data, key, options, callback); + + return promise; +}; + +function resolveContentValue(data, key, options, callback) { let content = (data && data[key] && data[key].content) || data[key]; const encoding = ((typeof data[key] === 'object' && data[key].encoding) || 'utf8') .toString() @@ -538,15 +562,26 @@ module.exports.resolveContent = (data, key, callback) => { callback(null, value); }); } else if (/^https?:\/\//i.test(content.path || content.href)) { - return resolveStream(nmfetch(content.path || content.href), callback); + if (options.disableUrlAccess) { + return setImmediate(() => { + const err = new Error('Url access rejected for ' + (content.path || content.href)); + err.code = errors.EURLACCESS; + callback(err); + }); + } + return resolveStream(nmfetch(content.path || content.href, { headers: content.httpHeaders, tls: content.tls }), callback); } else if (/^data:/i.test(content.path || content.href)) { const parsedDataUri = module.exports.parseDataURI(content.path || content.href); - if (!parsedDataUri || !parsedDataUri.data) { - return callback(null, Buffer.from(0)); - } - return callback(null, parsedDataUri.data); + return callback(null, parsedDataUri && parsedDataUri.data ? parsedDataUri.data : Buffer.alloc(0)); } else if (content.path) { + if (options.disableFileAccess) { + return setImmediate(() => { + const err = new Error('File access rejected for ' + content.path); + err.code = errors.EFILEACCESS; + callback(err); + }); + } return resolveStream(fs.createReadStream(content.path), callback); } } @@ -557,9 +592,7 @@ module.exports.resolveContent = (data, key, callback) => { // default action, return as is setImmediate(() => callback(null, content)); - - return promise; -}; +} /** * Copies properties from source objects to target objects diff --git a/node_modules/nodemailer/lib/shared/url.js b/node_modules/nodemailer/lib/shared/url.js new file mode 100644 index 00000000..116d5566 --- /dev/null +++ b/node_modules/nodemailer/lib/shared/url.js @@ -0,0 +1,151 @@ +'use strict'; + +// URL parsing wrapper. Prefers the WHATWG `URL` (a global on Node 10+, and +// available as `require('url').URL` since Node 6.13+) and only falls back to the +// legacy, deprecation-warning-emitting `url.parse()` / `url.resolve()` on ancient +// Node versions that predate the WHATWG implementation. +// +// The WHATWG `URL` exposes a different shape than the legacy parser, so results +// are normalized back into the legacy field names the rest of the codebase reads +// (`protocol`, `hostname`, `port`, `pathname`, `path`, `search`, `auth`, `query`, +// `href`). This keeps every existing call site unchanged. +// +// Known, accepted divergences from the legacy parser: +// - non-special schemes (smtp:/smtps:/direct:) are not host-lowercased by +// WHATWG; cosmetic only, SMTP/DNS hosts are case-insensitive. (IDNA mapping +// and IPv6 brackets are normalized back by normalizeHostname below.) +// - a literal unescaped ':' inside a password is percent-encoded by WHATWG; +// such passwords should be percent-encoded by the caller anyway. + +const urllib = require('url'); +const punycode = require('../punycode'); + +// WHATWG URL constructor if available, otherwise undefined (Node < 6.13). +const URLImpl = (typeof URL !== 'undefined' && URL) || urllib.URL; + +// Matches a "scheme:" not followed by "//" (and with something after it), used +// to re-insert the authority separator the legacy parser did not require. +const SLASHLESS_AUTHORITY = /^([a-zA-Z][a-zA-Z0-9+.-]*:)(?!\/\/)(.+)$/; + +// decodeURIComponent that never throws. Legacy url.parse() decodes the auth +// component but tolerates malformed percent sequences, so mirror that. +function safeDecode(str) { + try { + return decodeURIComponent(str); + } catch (_err) { + return str; + } +} + +// Derives the legacy-shaped bare hostname from a WHATWG URL. WHATWG keeps IPv6 +// literals bracketed ('[::1]') and, for non-special schemes (smtp:/smtps:/socks:), +// percent-encodes a non-ASCII host instead of IDNA-mapping it. Both forms are +// un-resolvable when handed to net/dns/http.request — which is what every call +// site does — so map them back to what legacy url.parse() returned: the bare +// address and the punycode form. Idempotent on plain ASCII and already-punycode +// hosts, so special-scheme hosts (already IDNA-mapped by WHATWG) pass through. +function normalizeHostname(raw) { + let hostname = raw || ''; + if (!hostname) { + // Host-less URL (e.g. 'direct:'): legacy returned '' here, not null; + // consumers do `hostname.length` / `'.' + hostname`, so keep it a string. + return ''; + } + if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') { + return hostname.slice(1, -1); + } + return punycode.toASCII(safeDecode(hostname)); +} + +module.exports.parse = (input, parseQueryString) => { + input = input || ''; + + if (!URLImpl) { + // Node < 6.13: no WHATWG URL available, use the legacy parser. + return urllib.parse(input, parseQueryString); + } + + // Legacy url.parse() parses a "user:pass@host:port" authority that follows + // the scheme even without the "//" separator, for schemes outside its + // built-in slashed-protocol list (smtp:/smtps:/socks:/...). The WHATWG + // parser instead treats a scheme not followed by "//" as an opaque path. + // Re-insert the "//" so slash-less connection/proxy URLs keep resolving to + // an authority, as they did before. This assumes a slash-authority scheme, + // which every consumer here uses (http/https/smtp/smtps/socks/direct); an + // opaque scheme like mailto:/data:/tel: would be mis-split, but none reach + // this module. + const slashless = SLASHLESS_AUTHORITY.exec(input); + const normalized = slashless ? slashless[1] + '//' + slashless[2] : input; + + let u; + try { + u = new URLImpl(normalized); + } catch (_err) { + // WHATWG rejects some input the legacy parser tolerated (empty/relative + // strings, scheme-relative '//host/path', out-of-range ports, ...). Fall + // back to the legacy parser so behavior — including the downstream errors + // callers rely on — is preserved. This is the only path that can still + // emit a deprecation warning; it fires for anything WHATWG cannot + // represent, including legitimate relative URLs, not just malformed input. + return urllib.parse(input, parseQueryString); + } + + const hostname = normalizeHostname(u.hostname); + const port = u.port || null; + const pathname = u.pathname || null; + const search = u.search || null; + + // Legacy `.auth` is the decoded "user[:pass]" string; WHATWG keeps the + // username/password percent-encoded, so decode to stay byte-compatible with + // existing consumers (parseConnectionUrl, Basic/Proxy-Authorization headers). + let auth = null; + if (u.username || u.password) { + // Gate on password too: legacy url.parse('smtps://:pass@host').auth was + // ':pass'. Dropping it would silently connect unauthenticated. + auth = safeDecode(u.username) + (u.password ? ':' + safeDecode(u.password) : ''); + } + + let query; + if (parseQueryString) { + // Mirror querystring.parse(): null-prototype object, repeated keys → array. + query = Object.create(null); + u.searchParams.forEach((value, key) => { + if (Object.prototype.hasOwnProperty.call(query, key)) { + if (Array.isArray(query[key])) { + query[key].push(value); + } else { + query[key] = [query[key], value]; + } + } else { + query[key] = value; + } + }); + } else { + query = search ? search.slice(1) : null; + } + + return { + protocol: u.protocol || null, + host: u.host || null, + hostname, + port, + pathname, + search, + path: (pathname || '') + (search || '') || null, + href: u.href, + auth, + query + }; +}; + +module.exports.resolve = (from, to) => { + if (!URLImpl) { + return urllib.resolve(from, to); + } + try { + return new URLImpl(to, from).href; + } catch (_err) { + // Malformed target — fall back to the legacy resolver. + return urllib.resolve(from, to); + } +}; diff --git a/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js b/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js index 36cd4bd8..4dc70132 100644 --- a/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js +++ b/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js @@ -6,7 +6,7 @@ const net = require('net'); const tls = require('tls'); -const urllib = require('url'); +const urllib = require('../shared/url'); const errors = require('../errors'); /** @@ -19,20 +19,29 @@ const errors = require('../errors'); * @param {String} proxyUrl proxy configuration, etg "http://proxy.host:3128/" * @param {Number} destinationPort Port to open in destination host * @param {String} destinationHost Destination hostname + * @param {Object} [tlsOptions] Optional TLS options for an HTTPS proxy (e.g. { rejectUnauthorized: false }) * @param {Function} callback Callback to run with the rocket object once connection is established */ -function httpProxyClient(proxyUrl, destinationPort, destinationHost, callback) { +function httpProxyClient(proxyUrl, destinationPort, destinationHost, tlsOptions, callback) { + if (typeof tlsOptions === 'function') { + callback = tlsOptions; + tlsOptions = {}; + } + tlsOptions = tlsOptions || {}; + const proxy = urllib.parse(proxyUrl); - const options = { + const connectOptions = { host: proxy.hostname, port: Number(proxy.port) ? Number(proxy.port) : proxy.protocol === 'https:' ? 443 : 80 }; let connect; if (proxy.protocol === 'https:') { - // we can use untrusted proxies as long as we verify actual SMTP certificates - options.rejectUnauthorized = false; + // Validate the proxy's TLS certificate by default. A caller that uses a + // self-signed proxy (e.g. integration tests) opts out explicitly with + // tls.rejectUnauthorized === false. + connectOptions.rejectUnauthorized = tlsOptions.rejectUnauthorized !== false; connect = tls.connect.bind(tls); } else { connect = net.connect.bind(net); @@ -62,7 +71,7 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, callback) { tempSocketErr(err); }; - socket = connect(options, () => { + socket = connect(connectOptions, () => { if (finished) { return; } diff --git a/node_modules/nodemailer/lib/smtp-connection/index.js b/node_modules/nodemailer/lib/smtp-connection/index.js index df5fd0a2..c201529c 100644 --- a/node_modules/nodemailer/lib/smtp-connection/index.js +++ b/node_modules/nodemailer/lib/smtp-connection/index.js @@ -51,9 +51,9 @@ function decodeServerResponse(str) { * * **requireTLS** - forces the client to use STARTTLS * * **name** - the name of the client server * * **localAddress** - outbound address to bind to (see: http://nodejs.org/api/net.html#net_net_connect_options_connectionlistener) - * * **greetingTimeout** - Time to wait in ms until greeting message is received from the server (defaults to 10000) - * * **connectionTimeout** - how many milliseconds to wait for the connection to establish - * * **socketTimeout** - Time of inactivity until the connection is closed (defaults to 1 hour) + * * **greetingTimeout** - Time to wait in ms until greeting message is received from the server (defaults to 30 seconds) + * * **connectionTimeout** - how many milliseconds to wait for the connection to establish (defaults to 2 minutes) + * * **socketTimeout** - Time of inactivity until the connection is closed (defaults to 10 minutes) * * **dnsTimeout** - Time to wait in ms for the DNS requests to be resolved (defaults to 30 seconds) * * **lmtp** - if true, uses LMTP instead of SMTP protocol * * **logger** - bunyan compatible logger interface @@ -211,6 +211,13 @@ class SMTPConnection extends EventEmitter { */ this._closing = false; + /** + * Message DATA stream currently piped to the socket, if any. Tracked so + * close() can unpipe it before tearing the socket down. + * @private + */ + this._currentDataStream = false; + /** * Callbacks for socket's listeners */ @@ -470,6 +477,17 @@ class SMTPConnection extends EventEmitter { const socket = (this._socket && this._socket.socket) || this._socket; + // Detach any in-flight DATA stream from the socket so the source stream + // can be garbage-collected once the socket is gone. + if (this._currentDataStream) { + try { + this._currentDataStream.unpipe(this._socket); + } catch (_E) { + // ignore + } + this._currentDataStream = false; + } + if (socket && !socket.destroyed) { try { // Clear socket timeout to prevent timer leaks @@ -820,7 +838,7 @@ class SMTPConnection extends EventEmitter { return; } - let data = (chunk || '').toString('binary'); + let data = chunk.toString('binary'); let lines = (this._remainder + data).split(/\r?\n/); let lastline; @@ -953,7 +971,9 @@ class SMTPConnection extends EventEmitter { */ _onEnd() { if (this._socket && !this._socket.destroyed) { - this._socket.destroy(); + // Peer sent FIN — finish our half of the close gracefully rather + // than destroying. 'close' fires after the OS finalizes teardown. + this._socket.end(); } } @@ -1005,6 +1025,15 @@ class SMTPConnection extends EventEmitter { opts.servername = this.servername; } + // Remove all listeners from the plain socket to allow proper garbage + // collection. Used on both the TLS-success path and the synchronous + // tls.connect() throw path; either way the plain socket is done. + const removePlainSocketListeners = () => { + socketPlain.removeListener('close', this._onSocketClose); + socketPlain.removeListener('end', this._onSocketEnd); + socketPlain.removeListener('error', this._onSocketError); + }; + this.upgrading = true; // tls.connect is not an asynchronous function however it may still throw errors and requires to be wrapped with try/catch try { @@ -1013,14 +1042,12 @@ class SMTPConnection extends EventEmitter { this.upgrading = false; this._socket.on('data', this._onSocketData); - // Remove all listeners from the plain socket to allow proper garbage collection - socketPlain.removeListener('close', this._onSocketClose); - socketPlain.removeListener('end', this._onSocketEnd); - socketPlain.removeListener('error', this._onSocketError); + removePlainSocketListeners(); return callback(null, true); }); } catch (err) { + removePlainSocketListeners(); return callback(err); } @@ -1297,6 +1324,7 @@ class SMTPConnection extends EventEmitter { }); } + this._currentDataStream = dataStream; dataStream.pipe(this._socket, { end: false }); @@ -1318,6 +1346,9 @@ class SMTPConnection extends EventEmitter { } dataStream.once('end', () => { + if (this._currentDataStream === dataStream) { + this._currentDataStream = false; + } this.logger.info( { tnx: 'message', diff --git a/node_modules/nodemailer/lib/smtp-transport/index.js b/node_modules/nodemailer/lib/smtp-transport/index.js index 5988fc22..bc07616f 100644 --- a/node_modules/nodemailer/lib/smtp-transport/index.js +++ b/node_modules/nodemailer/lib/smtp-transport/index.js @@ -71,13 +71,19 @@ class SMTPTransport extends EventEmitter { getAuth(authOpts) { if (!authOpts) { + if (this.auth && this.auth.oauth2 && this.mailer) { + // Transport-level auth is resolved in the constructor, before the Mail wrapper + // assigns `this.mailer`, so a provision callback registered with + // `transporter.set('oauth2_provision_cb', ...)` has to be re-checked here + this.auth.oauth2.provisionCallback = this.mailer.get('oauth2_provision_cb') || this.auth.oauth2.provisionCallback; + } return this.auth; } const authData = Object.assign( {}, this.options.auth && typeof this.options.auth === 'object' ? this.options.auth : {}, - authOpts && typeof authOpts === 'object' ? authOpts : {} + typeof authOpts === 'object' ? authOpts : {} ); if (Object.keys(authData).length === 0) { @@ -151,11 +157,20 @@ class SMTPTransport extends EventEmitter { const connection = new SMTPConnection(options); + let perCallAuth; + const cleanupPerCallAuth = () => { + if (perCallAuth && perCallAuth !== this.auth && perCallAuth.oauth2) { + perCallAuth.oauth2.removeAllListeners(); + } + perCallAuth = null; + }; + connection.once('error', err => { if (returned) { return; } returned = true; + cleanupPerCallAuth(); connection.close(); return callback(err); }); @@ -170,6 +185,7 @@ class SMTPTransport extends EventEmitter { return; } returned = true; + cleanupPerCallAuth(); // still have not returned, this means we have an unexpected connection close const err = new Error('Unexpected socket close'); if (connection && connection._socket && connection._socket.upgrading) { @@ -216,6 +232,7 @@ class SMTPTransport extends EventEmitter { connection.send(envelope, mail.message.createReadStream(), (err, info) => { returned = true; + cleanupPerCallAuth(); connection.close(); if (err) { this.logger.error( @@ -255,13 +272,11 @@ class SMTPTransport extends EventEmitter { return; } - const auth = this.getAuth(mail.data.auth); + perCallAuth = this.getAuth(mail.data.auth); - if (auth && (connection.allowsAuth || options.forceAuth)) { - connection.login(auth, err => { - if (auth && auth !== this.auth && auth.oauth2) { - auth.oauth2.removeAllListeners(); - } + if (perCallAuth && (connection.allowsAuth || options.forceAuth)) { + connection.login(perCallAuth, err => { + cleanupPerCallAuth(); if (returned) { return; } @@ -323,12 +338,20 @@ class SMTPTransport extends EventEmitter { const connection = new SMTPConnection(options); let returned = false; + let perCallAuth; + const cleanupPerCallAuth = () => { + if (perCallAuth && perCallAuth !== this.auth && perCallAuth.oauth2) { + perCallAuth.oauth2.removeAllListeners(); + } + perCallAuth = null; + }; connection.once('error', err => { if (returned) { return; } returned = true; + cleanupPerCallAuth(); connection.close(); return callback(err); }); @@ -338,6 +361,7 @@ class SMTPTransport extends EventEmitter { return; } returned = true; + cleanupPerCallAuth(); return callback(new Error('Connection closed')); }); @@ -346,6 +370,7 @@ class SMTPTransport extends EventEmitter { return; } returned = true; + cleanupPerCallAuth(); connection.quit(); return callback(null, true); }; @@ -355,10 +380,11 @@ class SMTPTransport extends EventEmitter { return; } - const authData = this.getAuth({}); + perCallAuth = this.getAuth({}); - if (authData && (connection.allowsAuth || options.forceAuth)) { - connection.login(authData, err => { + if (perCallAuth && (connection.allowsAuth || options.forceAuth)) { + connection.login(perCallAuth, err => { + cleanupPerCallAuth(); if (returned) { return; } @@ -371,11 +397,12 @@ class SMTPTransport extends EventEmitter { finalize(); }); - } else if (!authData && connection.allowsAuth && options.forceAuth) { + } else if (!perCallAuth && connection.allowsAuth && options.forceAuth) { const err = new Error('Authentication info was not provided'); err.code = errors.ENOAUTH; returned = true; + cleanupPerCallAuth(); connection.close(); return callback(err); } else { diff --git a/node_modules/nodemailer/lib/stream-transport/index.js b/node_modules/nodemailer/lib/stream-transport/index.js index 08d5ebd7..c000111f 100644 --- a/node_modules/nodemailer/lib/stream-transport/index.js +++ b/node_modules/nodemailer/lib/stream-transport/index.js @@ -2,6 +2,8 @@ const packageData = require('../../package.json'); const shared = require('../shared'); +const LeWindows = require('../mime-node/le-windows'); +const LeUnix = require('../mime-node/le-unix'); /** * Generates a Transport object for streaming @@ -63,6 +65,13 @@ class StreamTransport { try { stream = mail.message.createReadStream(); + if (this.options.newline) { + // apply the transport-level line ending transform; the message-level + // `newline` option is handled by MimeNode in createReadStream() + const sourceStream = stream; + stream = sourceStream.pipe(this.winbreak ? new LeWindows() : new LeUnix()); + sourceStream.once('error', err => stream.emit('error', err)); + } } catch (E) { this.logger.error( { diff --git a/node_modules/nodemailer/lib/xoauth2/index.js b/node_modules/nodemailer/lib/xoauth2/index.js index c1321db2..7c262fb0 100644 --- a/node_modules/nodemailer/lib/xoauth2/index.js +++ b/node_modules/nodemailer/lib/xoauth2/index.js @@ -33,6 +33,7 @@ const errors = require('../errors'); * @param {Number} options.expires Optional Access Token expire time in ms * @param {Number} options.timeout Optional TTL for Access Token in seconds * @param {Function} options.provisionCallback Function to run when a new access token is required + * @param {Object} options.tls Optional TLS options forwarded to the HTTPS token request. Defaults to strict cert validation; supply { rejectUnauthorized: false } only for self-hosted OAuth providers on private CAs. */ class XOAuth2 extends Stream { constructor(options, logger) { @@ -370,12 +371,23 @@ class XOAuth2 extends Stream { const chunks = []; let chunklen = 0; - const req = nmfetch(url, { + const fetchOptions = { method: 'post', headers: params.customHeaders, body: payload, allowErrorResponse: true - }); + }; + + // OAuth2 token endpoints are credential-bearing. lib/fetch already + // validates certs by default; pin rejectUnauthorized:true here so the + // token fetch stays strict, while still layering params.tls (the + // user's options.tls) on top so callers with a self-hosted provider on + // a private CA can override. + if (/^https:/i.test(url)) { + fetchOptions.tls = Object.assign({ rejectUnauthorized: true }, params.tls || {}); + } + + const req = nmfetch(url, fetchOptions); req.on('readable', () => { let chunk; diff --git a/node_modules/nodemailer/package.json b/node_modules/nodemailer/package.json index 5e7bb627..4c07b1fb 100644 --- a/node_modules/nodemailer/package.json +++ b/node_modules/nodemailer/package.json @@ -1,11 +1,11 @@ { "name": "nodemailer", - "version": "8.0.7", + "version": "9.0.0", "description": "Easy as cake e-mail sending from your Node.js applications", "main": "lib/nodemailer.js", "scripts": { - "test": "node --test --test-concurrency=1 test/**/*.test.js test/**/*-test.js", - "test:coverage": "c8 node --test --test-concurrency=1 test/**/*.test.js test/**/*-test.js", + "test": "node --test --test-concurrency=1 $(find test \\( -name '*-test.js' -o -name '*.test.js' \\))", + "test:coverage": "c8 node --test --test-concurrency=1 $(find test \\( -name '*-test.js' -o -name '*.test.js' \\))", "format": "prettier --write \"**/*.{js,json,md}\"", "format:check": "prettier --check \"**/*.{js,json,md}\"", "lint": "eslint .", @@ -27,19 +27,19 @@ }, "homepage": "https://nodemailer.com/", "devDependencies": { - "@aws-sdk/client-sesv2": "3.1037.0", + "@aws-sdk/client-sesv2": "3.1065.0", "bunyan": "1.8.15", "c8": "11.0.0", - "eslint": "10.2.1", + "eslint": "10.4.1", "eslint-config-prettier": "10.1.8", - "globals": "17.5.0", + "globals": "17.6.0", "libbase64": "1.3.0", "libmime": "5.3.8", "libqp": "2.1.1", - "prettier": "3.8.3", + "prettier": "3.8.4", "proxy": "1.0.2", "proxy-test-server": "1.0.0", - "smtp-server": "3.18.4" + "smtp-server": "3.18.5" }, "engines": { "node": ">=6.0.0"