nodemailer 10.0.4 → 10.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## [10.0.5](https://github.com/nodemailer/nodemailer/compare/v10.0.4...v10.0.5) (2026-09-11)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **addressparser:** parse comment-joined addresses in linear time ([c07f175](https://github.com/nodemailer/nodemailer/commit/c07f17518d25aca8ab2ad66968dcbca538c24b89))
9
+
3
10
  ## [10.0.4](https://github.com/nodemailer/nodemailer/compare/v10.0.3...v10.0.4) (2026-09-11)
4
11
 
5
12
 
@@ -134,6 +134,12 @@ function _handleAddress(tokens, depth) {
134
134
  textWasQuoted: []
135
135
  };
136
136
  let insideQuotes = false;
137
+ // Last character of the run each state is currently accumulating. Reading it back off
138
+ // the accumulator with slice(-1) makes the engine flatten the whole growing string on
139
+ // every token, which is quadratic over an address built from many comment-joined atoms
140
+ // (GHSA-prgh-xp8r-p3m5). A run only ever grows by the token appended below, so the
141
+ // character is carried along instead of re-read.
142
+ const lastChars = { address: '', comment: '', group: '', text: '' };
137
143
  // Filter out <addresses>, (comments) and regular text
138
144
  for (let i = 0, len = tokens.length; i < len; i++) {
139
145
  const token = tokens[i];
@@ -177,15 +183,19 @@ function _handleAddress(tokens, depth) {
177
183
  const joins = prevToken &&
178
184
  prevToken.noBreak &&
179
185
  parts.length &&
180
- (prevToken.value !== ')' || parts[parts.length - 1].slice(-1) === '@' || token.value.charAt(0) === '@');
186
+ (prevToken.value !== ')' || lastChars[state] === '@' || token.value.charAt(0) === '@');
181
187
  if (joins) {
182
188
  data[state][data[state].length - 1] += token.value;
189
+ if (token.value) {
190
+ lastChars[state] = token.value.charAt(token.value.length - 1);
191
+ }
183
192
  if (state === 'text' && insideQuotes) {
184
193
  data.textWasQuoted[data.textWasQuoted.length - 1] = true;
185
194
  }
186
195
  }
187
196
  else {
188
197
  data[state].push(token.value);
198
+ lastChars[state] = token.value.charAt(token.value.length - 1);
189
199
  if (state === 'text') {
190
200
  data.textWasQuoted.push(insideQuotes);
191
201
  }
@@ -1,3 +1,3 @@
1
1
  export declare const name = "nodemailer";
2
- export declare const version = "10.0.4";
2
+ export declare const version = "10.0.5";
3
3
  export declare const homepage = "https://nodemailer.com/";
@@ -3,5 +3,5 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.homepage = exports.version = exports.name = void 0;
5
5
  exports.name = 'nodemailer';
6
- exports.version = '10.0.4';
6
+ exports.version = '10.0.5';
7
7
  exports.homepage = 'https://nodemailer.com/';
@@ -131,6 +131,12 @@ function _handleAddress(tokens, depth) {
131
131
  textWasQuoted: []
132
132
  };
133
133
  let insideQuotes = false;
134
+ // Last character of the run each state is currently accumulating. Reading it back off
135
+ // the accumulator with slice(-1) makes the engine flatten the whole growing string on
136
+ // every token, which is quadratic over an address built from many comment-joined atoms
137
+ // (GHSA-prgh-xp8r-p3m5). A run only ever grows by the token appended below, so the
138
+ // character is carried along instead of re-read.
139
+ const lastChars = { address: '', comment: '', group: '', text: '' };
134
140
  // Filter out <addresses>, (comments) and regular text
135
141
  for (let i = 0, len = tokens.length; i < len; i++) {
136
142
  const token = tokens[i];
@@ -174,15 +180,19 @@ function _handleAddress(tokens, depth) {
174
180
  const joins = prevToken &&
175
181
  prevToken.noBreak &&
176
182
  parts.length &&
177
- (prevToken.value !== ')' || parts[parts.length - 1].slice(-1) === '@' || token.value.charAt(0) === '@');
183
+ (prevToken.value !== ')' || lastChars[state] === '@' || token.value.charAt(0) === '@');
178
184
  if (joins) {
179
185
  data[state][data[state].length - 1] += token.value;
186
+ if (token.value) {
187
+ lastChars[state] = token.value.charAt(token.value.length - 1);
188
+ }
180
189
  if (state === 'text' && insideQuotes) {
181
190
  data.textWasQuoted[data.textWasQuoted.length - 1] = true;
182
191
  }
183
192
  }
184
193
  else {
185
194
  data[state].push(token.value);
195
+ lastChars[state] = token.value.charAt(token.value.length - 1);
186
196
  if (state === 'text') {
187
197
  data.textWasQuoted.push(insideQuotes);
188
198
  }
@@ -1,3 +1,3 @@
1
1
  export declare const name = "nodemailer";
2
- export declare const version = "10.0.4";
2
+ export declare const version = "10.0.5";
3
3
  export declare const homepage = "https://nodemailer.com/";
@@ -1,4 +1,4 @@
1
1
  // Generated by scripts/build.js from package.json. Do not edit by hand.
2
2
  export const name = 'nodemailer';
3
- export const version = '10.0.4';
3
+ export const version = '10.0.5';
4
4
  export const homepage = 'https://nodemailer.com/';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodemailer",
3
- "version": "10.0.4",
3
+ "version": "10.0.5",
4
4
  "description": "Easy as cake e-mail sending from your Node.js applications",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/nodemailer.js",