9 Commits
v9 ... v12

Author SHA1 Message Date
Dawid Dziurla
949ec3d78a node_modules: update (#270)
Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com>
2026-03-10 10:09:25 +01:00
dependabot[bot]
f0e039bce4 build(deps): bump nodemailer from 8.0.1 to 8.0.2 (#269)
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 8.0.1 to 8.0.2.
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v8.0.1...v8.0.2)

---
updated-dependencies:
- dependency-name: nodemailer
  dependency-version: 8.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-10 10:08:28 +01:00
Fritz Elfert
41125078ce Misc. fixes (#267)
* Misc. fixes

- Do not expose 'username' to a potential recipient.
- Make 'from' mandatory, because there is no fallback anymore.
- Validate 'from' to make shure, it contains an email address.
- Avoid namespace pollution: Use let instead of var.

* Suggested changes from codereview
2026-03-10 10:08:10 +01:00
dependabot[bot]
e035119249 build(deps): bump minimatch from 3.1.3 to 3.1.5 (#265)
Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.1.3 to 3.1.5.
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.1.3...v3.1.5)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-version: 3.1.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-27 13:42:35 +01:00
Spiros Kalogeropoulos
ba302ba66e Add support for custom email headers via JSON input (#264)
* Adding custom header with JSON support

* Adding headers in test workflow

---------

Co-authored-by: W506810_wexinc <spiros.kalogeropoulos@wexinc.com>
2026-02-27 13:38:27 +01:00
Dawid Dziurla
7c60f8f904 node_modules: update (#263)
Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com>
2026-02-25 14:17:23 +01:00
dependabot[bot]
09ee758a9b build(deps): bump minimatch from 3.1.2 to 3.1.3 (#262)
Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.1.2 to 3.1.3.
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.3)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-version: 3.1.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-25 14:16:23 +01:00
Dawid Dziurla
5335a581b9 node_modules: update (#261)
Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com>
2026-02-09 10:59:27 +01:00
dependabot[bot]
63e792e90a build(deps): bump nodemailer from 8.0.0 to 8.0.1 (#260)
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 8.0.0 to 8.0.1.
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v8.0.0...v8.0.1)

---
updated-dependencies:
- dependency-name: nodemailer
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-09 10:57:57 +01:00
15 changed files with 282 additions and 138 deletions

View File

@@ -46,6 +46,7 @@ jobs:
convert_markdown: true convert_markdown: true
attachments: package.json,action.yml attachments: package.json,action.yml
priority: high priority: high
headers: '{"X-My-Test-Header": "Passed"}'
- name: Get mail - name: Get mail
run: | run: |

View File

@@ -39,10 +39,11 @@ Some features:
password: ${{secrets.MAIL_PASSWORD}} password: ${{secrets.MAIL_PASSWORD}}
# Required mail subject: # Required mail subject:
subject: Github Actions job result subject: Github Actions job result
# Required recipients' addresses: # Optional recipients' addresses:
to: obiwan@example.com,yoda@example.com to: obiwan@example.com,yoda@example.com
# Required sender full name (address can be skipped): # Required sender (Either: "Plain Simple Name <user@doma.in>" or just "user@doma.in" (without the <>))
from: Luke Skywalker # <user@example.com> # Important: '<' and '>' are special chars in yaml. Therefore this string should be quoted
from: 'Luke Skywalker <user@example.com>'
# Optional plain body: # Optional plain body:
body: Build job of ${{github.repository}} completed successfully! body: Build job of ${{github.repository}} completed successfully!
# Optional HTML body read from file: # Optional HTML body read from file:
@@ -63,6 +64,8 @@ Some features:
attachments: attachments.zip,git.diff,./dist/static/*.js attachments: attachments.zip,git.diff,./dist/static/*.js
# Optional priority: 'high', 'normal' (default) or 'low' # Optional priority: 'high', 'normal' (default) or 'low'
priority: low priority: low
# Optional custom headers:
headers: '{"X-Priority": "3 (Normal)", "X-My-Header": "MyValue"}'
# Optional nodemailerlog: true/false # Optional nodemailerlog: true/false
nodemailerlog: false nodemailerlog: false
# Optional nodemailerdebug: true/false if true lognodem will also be set true # Optional nodemailerdebug: true/false if true lognodem will also be set true

View File

@@ -25,7 +25,7 @@ inputs:
description: Recipients mail addresses (separated with comma) description: Recipients mail addresses (separated with comma)
required: false required: false
from: from:
description: Full name of mail sender (might be with an email address specified in <>) description: 'Either a plain email address, or full name of the sender, followed by whitespace, followed by email address enclosed in <>'
required: true required: true
body: body:
description: Body of mail message (might be a filename prefixed with file:// to read from) description: Body of mail message (might be a filename prefixed with file:// to read from)
@@ -57,6 +57,9 @@ inputs:
priority: priority:
description: Set Priority level for the mail message to 'high', 'normal' (default) or 'low' description: Set Priority level for the mail message to 'high', 'normal' (default) or 'low'
required: false required: false
headers:
description: 'Custom headers for the mail message as a JSON string (e.g., {"X-Custom-Header": "value"})'
required: false
nodemailerlog: nodemailerlog:
description: Log option for nodemailer description: Log option for nodemailer
required: false required: false

20
main.js
View File

@@ -23,14 +23,6 @@ function getText(textOrFile, convertMarkdown) {
return text; return text;
} }
function getFrom(from, username) {
if (from.match(/.+ <.+@.+>/)) {
return from;
}
return `"${from}" <${username}>`;
}
async function getAttachments(attachments) { async function getAttachments(attachments) {
const globber = await glob.create(attachments.split(",").join("\n")); const globber = await glob.create(attachments.split(",").join("\n"));
const files = await globber.glob(); const files = await globber.glob();
@@ -115,6 +107,13 @@ async function main() {
required: false, required: false,
}); });
const envelopeTo = core.getInput("envelope_to", { required: false }); const envelopeTo = core.getInput("envelope_to", { required: false });
const headers = core.getInput("headers", { required: false });
// Basic check for an email sender address
// Either: "Plain Simple Name <user@doma.in>" or just "user@doma.in" (without the <>)
if (!(/^([^<>@\s]+\s+)+<[^@\s>]+@[^@\s>]+>$/.test(from) || /^[^<>@\s]+@[^@\s<>]+$/.test(from))) {
throw new Error("'from' address is invalid");
}
// if neither to, cc or bcc is provided, throw error // if neither to, cc or bcc is provided, throw error
if (!to && !cc && !bcc) { if (!to && !cc && !bcc) {
@@ -149,11 +148,11 @@ async function main() {
proxy: process.env.HTTP_PROXY, proxy: process.env.HTTP_PROXY,
}); });
var i = 1; let i = 1;
while (true) { while (true) {
try { try {
const info = await transport.sendMail({ const info = await transport.sendMail({
from: getFrom(from, username), from: from,
to: to, to: to,
subject: getText(subject, false), subject: getText(subject, false),
cc: cc ? cc : undefined, cc: cc ? cc : undefined,
@@ -166,6 +165,7 @@ async function main() {
? getText(htmlBody, convertMarkdown) ? getText(htmlBody, convertMarkdown)
: undefined, : undefined,
priority: priority ? priority : undefined, priority: priority ? priority : undefined,
headers: headers ? JSON.parse(headers) : undefined,
attachments: attachments attachments: attachments
? await getAttachments(attachments) ? await getAttachments(attachments)
: undefined, : undefined,

12
node_modules/.package-lock.json generated vendored
View File

@@ -185,9 +185,9 @@
} }
}, },
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.2", "version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
@@ -197,9 +197,9 @@
} }
}, },
"node_modules/nodemailer": { "node_modules/nodemailer": {
"version": "8.0.0", "version": "8.0.2",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.0.tgz", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.2.tgz",
"integrity": "sha512-xvVJf/f0bzmNpnRIbhCp/IKxaHgJ6QynvUbLXzzMRPG3LDQr5oXkYuw4uDFyFYs8cge8agwwrJAXZsd4hhMquw==", "integrity": "sha512-zbj002pZAIkWQFxyAaqoxvn+zoIwRnS40hgjqTXudKOOJkiFFgBeNqjgD3/YCR12sZnrghWYBY+yP1ZucdDRpw==",
"license": "MIT-0", "license": "MIT-0",
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"

37
node_modules/minimatch/README.md generated vendored
View File

@@ -10,6 +10,43 @@ This is the matching library used internally by npm.
It works by converting glob expressions into JavaScript `RegExp` It works by converting glob expressions into JavaScript `RegExp`
objects. objects.
## Important Security Consideration!
> [!WARNING]
> This library uses JavaScript regular expressions. Please read
> the following warning carefully, and be thoughtful about what
> you provide to this library in production systems.
_Any_ library in JavaScript that deals with matching string
patterns using regular expressions will be subject to
[ReDoS](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS)
if the pattern is generated using untrusted input.
Efforts have been made to mitigate risk as much as is feasible in
such a library, providing maximum recursion depths and so forth,
but these measures can only ultimately protect against accidents,
not malice. A dedicated attacker can _always_ find patterns that
cannot be defended against by a bash-compatible glob pattern
matching system that uses JavaScript regular expressions.
To be extremely clear:
> [!WARNING]
> **If you create a system where you take user input, and use
> that input as the source of a Regular Expression pattern, in
> this or any extant glob matcher in JavaScript, you will be
> pwned.**
A future version of this library _may_ use a different matching
algorithm which does not exhibit backtracking problems. If and
when that happens, it will likely be a sweeping change, and those
improvements will **not** be backported to legacy versions.
In the near term, it is not reasonable to continue to play
whack-a-mole with security advisories, and so any future ReDoS
reports will be considered "working as intended", and resolved
entirely by this warning.
## Usage ## Usage
```javascript ```javascript

262
node_modules/minimatch/minimatch.js generated vendored
View File

@@ -142,6 +142,8 @@ function Minimatch (pattern, options) {
} }
this.options = options this.options = options
this.maxGlobstarRecursion = options.maxGlobstarRecursion !== undefined
? options.maxGlobstarRecursion : 200
this.set = [] this.set = []
this.pattern = pattern this.pattern = pattern
this.regexp = null this.regexp = null
@@ -390,6 +392,9 @@ function parse (pattern, isSub) {
continue continue
} }
// coalesce consecutive non-globstar * characters
if (c === '*' && stateChar === '*') continue
// if we already have a stateChar, then it means // if we already have a stateChar, then it means
// that there was something like ** or +? in there. // that there was something like ** or +? in there.
// Handle the stateChar, then proceed with this one. // Handle the stateChar, then proceed with this one.
@@ -784,19 +789,163 @@ Minimatch.prototype.match = function match (f, partial) {
// out of pattern, then that's fine, as long as all // out of pattern, then that's fine, as long as all
// the parts match. // the parts match.
Minimatch.prototype.matchOne = function (file, pattern, partial) { Minimatch.prototype.matchOne = function (file, pattern, partial) {
var options = this.options if (pattern.indexOf(GLOBSTAR) !== -1) {
return this._matchGlobstar(file, pattern, partial, 0, 0)
}
return this._matchOne(file, pattern, partial, 0, 0)
}
this.debug('matchOne', Minimatch.prototype._matchGlobstar = function (file, pattern, partial, fileIndex, patternIndex) {
{ 'this': this, file: file, pattern: pattern }) var i
this.debug('matchOne', file.length, pattern.length) // find first globstar from patternIndex
var firstgs = -1
for (i = patternIndex; i < pattern.length; i++) {
if (pattern[i] === GLOBSTAR) { firstgs = i; break }
}
for (var fi = 0, // find last globstar
pi = 0, var lastgs = -1
fl = file.length, for (i = pattern.length - 1; i >= 0; i--) {
pl = pattern.length if (pattern[i] === GLOBSTAR) { lastgs = i; break }
; (fi < fl) && (pi < pl) }
; fi++, pi++) {
var head = pattern.slice(patternIndex, firstgs)
var body = partial ? pattern.slice(firstgs + 1) : pattern.slice(firstgs + 1, lastgs)
var tail = partial ? [] : pattern.slice(lastgs + 1)
// check the head
if (head.length) {
var fileHead = file.slice(fileIndex, fileIndex + head.length)
if (!this._matchOne(fileHead, head, partial, 0, 0)) {
return false
}
fileIndex += head.length
}
// check the tail
var fileTailMatch = 0
if (tail.length) {
if (tail.length + fileIndex > file.length) return false
var tailStart = file.length - tail.length
if (this._matchOne(file, tail, partial, tailStart, 0)) {
fileTailMatch = tail.length
} else {
// affordance for stuff like a/**/* matching a/b/
if (file[file.length - 1] !== '' ||
fileIndex + tail.length === file.length) {
return false
}
tailStart--
if (!this._matchOne(file, tail, partial, tailStart, 0)) {
return false
}
fileTailMatch = tail.length + 1
}
}
// if body is empty (single ** between head and tail)
if (!body.length) {
var sawSome = !!fileTailMatch
for (i = fileIndex; i < file.length - fileTailMatch; i++) {
var f = String(file[i])
sawSome = true
if (f === '.' || f === '..' ||
(!this.options.dot && f.charAt(0) === '.')) {
return false
}
}
return partial || sawSome
}
// split body into segments at each GLOBSTAR
var bodySegments = [[[], 0]]
var currentBody = bodySegments[0]
var nonGsParts = 0
var nonGsPartsSums = [0]
for (var bi = 0; bi < body.length; bi++) {
var b = body[bi]
if (b === GLOBSTAR) {
nonGsPartsSums.push(nonGsParts)
currentBody = [[], 0]
bodySegments.push(currentBody)
} else {
currentBody[0].push(b)
nonGsParts++
}
}
var idx = bodySegments.length - 1
var fileLength = file.length - fileTailMatch
for (var si = 0; si < bodySegments.length; si++) {
bodySegments[si][1] = fileLength -
(nonGsPartsSums[idx--] + bodySegments[si][0].length)
}
return !!this._matchGlobStarBodySections(
file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch
)
}
// return false for "nope, not matching"
// return null for "not matching, cannot keep trying"
Minimatch.prototype._matchGlobStarBodySections = function (
file, bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail
) {
var bs = bodySegments[bodyIndex]
if (!bs) {
// just make sure there are no bad dots
for (var i = fileIndex; i < file.length; i++) {
sawTail = true
var f = file[i]
if (f === '.' || f === '..' ||
(!this.options.dot && f.charAt(0) === '.')) {
return false
}
}
return sawTail
}
var body = bs[0]
var after = bs[1]
while (fileIndex <= after) {
var m = this._matchOne(
file.slice(0, fileIndex + body.length),
body,
partial,
fileIndex,
0
)
// if limit exceeded, no match. intentional false negative,
// acceptable break in correctness for security.
if (m && globStarDepth < this.maxGlobstarRecursion) {
var sub = this._matchGlobStarBodySections(
file, bodySegments,
fileIndex + body.length, bodyIndex + 1,
partial, globStarDepth + 1, sawTail
)
if (sub !== false) {
return sub
}
}
var f = file[fileIndex]
if (f === '.' || f === '..' ||
(!this.options.dot && f.charAt(0) === '.')) {
return false
}
fileIndex++
}
return partial || null
}
Minimatch.prototype._matchOne = function (file, pattern, partial, fileIndex, patternIndex) {
var fi, pi, fl, pl
for (
fi = fileIndex, pi = patternIndex, fl = file.length, pl = pattern.length
; (fi < fl) && (pi < pl)
; fi++, pi++
) {
this.debug('matchOne loop') this.debug('matchOne loop')
var p = pattern[pi] var p = pattern[pi]
var f = file[fi] var f = file[fi]
@@ -806,87 +955,7 @@ Minimatch.prototype.matchOne = function (file, pattern, partial) {
// should be impossible. // should be impossible.
// some invalid regexp stuff in the set. // some invalid regexp stuff in the set.
/* istanbul ignore if */ /* istanbul ignore if */
if (p === false) return false if (p === false || p === GLOBSTAR) return false
if (p === GLOBSTAR) {
this.debug('GLOBSTAR', [pattern, p, f])
// "**"
// a/**/b/**/c would match the following:
// a/b/x/y/z/c
// a/x/y/z/b/c
// a/b/x/b/x/c
// a/b/c
// To do this, take the rest of the pattern after
// the **, and see if it would match the file remainder.
// If so, return success.
// If not, the ** "swallows" a segment, and try again.
// This is recursively awful.
//
// a/**/b/**/c matching a/b/x/y/z/c
// - a matches a
// - doublestar
// - matchOne(b/x/y/z/c, b/**/c)
// - b matches b
// - doublestar
// - matchOne(x/y/z/c, c) -> no
// - matchOne(y/z/c, c) -> no
// - matchOne(z/c, c) -> no
// - matchOne(c, c) yes, hit
var fr = fi
var pr = pi + 1
if (pr === pl) {
this.debug('** at the end')
// a ** at the end will just swallow the rest.
// We have found a match.
// however, it will not swallow /.x, unless
// options.dot is set.
// . and .. are *never* matched by **, for explosively
// exponential reasons.
for (; fi < fl; fi++) {
if (file[fi] === '.' || file[fi] === '..' ||
(!options.dot && file[fi].charAt(0) === '.')) return false
}
return true
}
// ok, let's see if we can swallow whatever we can.
while (fr < fl) {
var swallowee = file[fr]
this.debug('\nglobstar while', file, fr, pattern, pr, swallowee)
// XXX remove this slice. Just pass the start index.
if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
this.debug('globstar found match!', fr, fl, swallowee)
// found a match.
return true
} else {
// can't swallow "." or ".." ever.
// can only swallow ".foo" when explicitly asked.
if (swallowee === '.' || swallowee === '..' ||
(!options.dot && swallowee.charAt(0) === '.')) {
this.debug('dot detected!', file, fr, pattern, pr)
break
}
// ** swallows a segment, and continue.
this.debug('globstar swallow a segment, and continue')
fr++
}
}
// no match was found.
// However, in partial mode, we can't say this is necessarily over.
// If there's more *pattern* left, then
/* istanbul ignore if */
if (partial) {
// ran out of file
this.debug('\n>>> no match, partial?', file, fr, pattern, pr)
if (fr === fl) return true
}
return false
}
// something other than ** // something other than **
// non-magic patterns just have to match exactly // non-magic patterns just have to match exactly
@@ -903,17 +972,6 @@ Minimatch.prototype.matchOne = function (file, pattern, partial) {
if (!hit) return false if (!hit) return false
} }
// Note: ending in / means that we'll get a final ""
// at the end of the pattern. This can only match a
// corresponding "" at the end of the file.
// If the file ends in /, then it can only match a
// a pattern that ends in /, unless the pattern just
// doesn't have any more for it. But, a/b/ should *not*
// match "a/b/*", even though "" matches against the
// [^/]*? pattern, except in partial mode, where it might
// simply not be reached yet.
// However, a/b/ should still satisfy a/*
// now either we fell off the end of the pattern, or we're done. // now either we fell off the end of the pattern, or we're done.
if (fi === fl && pi === pl) { if (fi === fl && pi === pl) {
// ran out of pattern and filename at the same time. // ran out of pattern and filename at the same time.

View File

@@ -2,9 +2,9 @@
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)", "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)",
"name": "minimatch", "name": "minimatch",
"description": "a glob matcher in javascript", "description": "a glob matcher in javascript",
"version": "3.1.2", "version": "3.1.5",
"publishConfig": { "publishConfig": {
"tag": "v3-legacy" "tag": "legacy-v3"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

16
node_modules/nodemailer/CHANGELOG.md generated vendored
View File

@@ -1,5 +1,21 @@
# CHANGELOG # CHANGELOG
## [8.0.2](https://github.com/nodemailer/nodemailer/compare/v8.0.1...v8.0.2) (2026-03-09)
### Bug Fixes
* merge fragmented display names with unquoted commas in addressparser ([fe27f7f](https://github.com/nodemailer/nodemailer/commit/fe27f7fd57f7587d897274438da2f628ad0ad7d9))
## [8.0.1](https://github.com/nodemailer/nodemailer/compare/v8.0.0...v8.0.1) (2026-02-07)
### Bug Fixes
* absorb TLS errors during socket teardown ([7f8dde4](https://github.com/nodemailer/nodemailer/commit/7f8dde41438c66b8311e888fa5f8c518fcaba6f1))
* absorb TLS errors during socket teardown ([381f628](https://github.com/nodemailer/nodemailer/commit/381f628d55e62bb3131bd2a452fa1ce00bc48aea))
* Add Gmail Workspace service configuration ([#1787](https://github.com/nodemailer/nodemailer/issues/1787)) ([dc97ede](https://github.com/nodemailer/nodemailer/commit/dc97ede417b3030b311771541b1f17f5ca76bcbf))
## [8.0.0](https://github.com/nodemailer/nodemailer/compare/v7.0.13...v8.0.0) (2026-02-04) ## [8.0.0](https://github.com/nodemailer/nodemailer/compare/v7.0.13...v8.0.0) (2026-02-04)

View File

@@ -361,6 +361,20 @@ function addressparser(str, options) {
} }
}); });
// Merge fragments from unquoted display names containing commas/semicolons.
// When "Joe Foo, PhD <joe@example.com>" is split on the comma, it produces
// [{name:"Joe Foo", address:""}, {name:"PhD", address:"joe@example.com"}].
// Detect this pattern and recombine: a name-only entry followed by an entry
// that has both a name and an address (from angle-bracket notation).
for (let i = parsedAddresses.length - 2; i >= 0; i--) {
let current = parsedAddresses[i];
let next = parsedAddresses[i + 1];
if (current.address === '' && current.name && !current.group && next.address && next.name && !next.group) {
next.name = current.name + ', ' + next.name;
parsedAddresses.splice(i, 1);
}
}
if (options.flatten) { if (options.flatten) {
let addresses = []; let addresses = [];
let walkAddressList = list => { let walkAddressList = list => {

View File

@@ -15,6 +15,7 @@ const CONNECTION_TIMEOUT = 2 * 60 * 1000; // how much to wait for the connection
const SOCKET_TIMEOUT = 10 * 60 * 1000; // how much to wait for socket inactivity before disconnecting the client const SOCKET_TIMEOUT = 10 * 60 * 1000; // how much to wait for socket inactivity before disconnecting the client
const GREETING_TIMEOUT = 30 * 1000; // how much to wait after connection is established but SMTP greeting is not receieved const GREETING_TIMEOUT = 30 * 1000; // how much to wait after connection is established but SMTP greeting is not receieved
const DNS_TIMEOUT = 30 * 1000; // how much to wait for resolveHostname const DNS_TIMEOUT = 30 * 1000; // how much to wait for resolveHostname
const TEARDOWN_NOOP = () => {}; // reusable no-op handler for absorbing errors during socket teardown
/** /**
* Generates a SMTP connection object * Generates a SMTP connection object
@@ -505,6 +506,9 @@ class SMTPConnection extends EventEmitter {
socket.removeListener('end', this._onSocketEnd); socket.removeListener('end', this._onSocketEnd);
socket.removeListener('error', this._onSocketError); socket.removeListener('error', this._onSocketError);
socket.removeListener('error', this._onConnectionSocketError); socket.removeListener('error', this._onConnectionSocketError);
// Absorb errors that may fire during socket teardown (e.g. server
// sending cleartext after TLS shutdown triggers ERR_SSL_BAD_RECORD_TYPE)
socket.on('error', TEARDOWN_NOOP);
socket[closeMethod](); socket[closeMethod]();
} catch (_E) { } catch (_E) {
// just ignore // just ignore

View File

@@ -147,6 +147,14 @@
"secure": true "secure": true
}, },
"GmailWorkspace": {
"description": "Gmail Workspace",
"aliases": ["Google Workspace Mail"],
"host": "smtp-relay.gmail.com",
"port": 465,
"secure": true
},
"GMX": { "GMX": {
"description": "GMX Mail", "description": "GMX Mail",
"domains": ["gmx.com", "gmx.net", "gmx.de"], "domains": ["gmx.com", "gmx.net", "gmx.de"],

12
node_modules/nodemailer/package.json generated vendored
View File

@@ -1,6 +1,6 @@
{ {
"name": "nodemailer", "name": "nodemailer",
"version": "8.0.0", "version": "8.0.2",
"description": "Easy as cake e-mail sending from your Node.js applications", "description": "Easy as cake e-mail sending from your Node.js applications",
"main": "lib/nodemailer.js", "main": "lib/nodemailer.js",
"scripts": { "scripts": {
@@ -26,12 +26,12 @@
}, },
"homepage": "https://nodemailer.com/", "homepage": "https://nodemailer.com/",
"devDependencies": { "devDependencies": {
"@aws-sdk/client-sesv2": "3.975.0", "@aws-sdk/client-sesv2": "3.1004.0",
"bunyan": "1.8.15", "bunyan": "1.8.15",
"c8": "10.1.3", "c8": "11.0.0",
"eslint": "9.39.2", "eslint": "10.0.3",
"eslint-config-prettier": "10.1.8", "eslint-config-prettier": "10.1.8",
"globals": "17.1.0", "globals": "17.4.0",
"libbase64": "1.3.0", "libbase64": "1.3.0",
"libmime": "5.3.7", "libmime": "5.3.7",
"libqp": "2.1.1", "libqp": "2.1.1",
@@ -39,7 +39,7 @@
"prettier": "3.8.1", "prettier": "3.8.1",
"proxy": "1.0.2", "proxy": "1.0.2",
"proxy-test-server": "1.0.0", "proxy-test-server": "1.0.0",
"smtp-server": "3.18.0" "smtp-server": "3.18.1"
}, },
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"

14
package-lock.json generated
View File

@@ -8,7 +8,7 @@
"dependencies": { "dependencies": {
"@actions/core": "^3.0.0", "@actions/core": "^3.0.0",
"@actions/glob": "^0.6.1", "@actions/glob": "^0.6.1",
"nodemailer": "^8.0.0", "nodemailer": "^8.0.2",
"showdown": "^1.9.1" "showdown": "^1.9.1"
} }
}, },
@@ -194,9 +194,9 @@
} }
}, },
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.2", "version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
@@ -206,9 +206,9 @@
} }
}, },
"node_modules/nodemailer": { "node_modules/nodemailer": {
"version": "8.0.0", "version": "8.0.2",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.0.tgz", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.2.tgz",
"integrity": "sha512-xvVJf/f0bzmNpnRIbhCp/IKxaHgJ6QynvUbLXzzMRPG3LDQr5oXkYuw4uDFyFYs8cge8agwwrJAXZsd4hhMquw==", "integrity": "sha512-zbj002pZAIkWQFxyAaqoxvn+zoIwRnS40hgjqTXudKOOJkiFFgBeNqjgD3/YCR12sZnrghWYBY+yP1ZucdDRpw==",
"license": "MIT-0", "license": "MIT-0",
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"

View File

@@ -5,7 +5,7 @@
"dependencies": { "dependencies": {
"@actions/core": "^3.0.0", "@actions/core": "^3.0.0",
"@actions/glob": "^0.6.1", "@actions/glob": "^0.6.1",
"nodemailer": "^8.0.0", "nodemailer": "^8.0.2",
"showdown": "^1.9.1" "showdown": "^1.9.1"
} }
} }