@mavogel/awscdk-rootmail 0.1.8 → 0.1.9

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.
Files changed (166) hide show
  1. package/.jsii +10 -10
  2. package/lib/rootmail.js +1 -1
  3. package/lib/ses-receive.js +1 -1
  4. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-cjs/index.js +5 -5
  5. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-es/schemas/schemas_0.js +4 -4
  6. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/DescribeDeliveryDestinationsCommand.d.ts +1 -0
  7. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/GetDeliveryDestinationCommand.d.ts +1 -0
  8. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/PutAccountPolicyCommand.d.ts +4 -1
  9. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/PutDeliveryDestinationCommand.d.ts +2 -0
  10. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/models/models_0.d.ts +80 -36
  11. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/ts3.4/models/models_0.d.ts +2 -0
  12. package/node_modules/@aws-sdk/client-cloudwatch-logs/package.json +1 -1
  13. package/node_modules/@aws-sdk/client-route-53/dist-cjs/index.js +1 -1
  14. package/node_modules/@aws-sdk/client-route-53/package.json +1 -1
  15. package/node_modules/@aws-sdk/client-s3/dist-cjs/index.js +2 -1
  16. package/node_modules/@aws-sdk/client-s3/dist-es/models/enums.js +1 -0
  17. package/node_modules/@aws-sdk/client-s3/dist-types/commands/GetBucketInventoryConfigurationCommand.d.ts +1 -1
  18. package/node_modules/@aws-sdk/client-s3/dist-types/commands/ListBucketInventoryConfigurationsCommand.d.ts +1 -1
  19. package/node_modules/@aws-sdk/client-s3/dist-types/commands/PutBucketInventoryConfigurationCommand.d.ts +1 -1
  20. package/node_modules/@aws-sdk/client-s3/dist-types/models/enums.d.ts +1 -0
  21. package/node_modules/@aws-sdk/client-s3/dist-types/ts3.4/models/enums.d.ts +1 -0
  22. package/node_modules/@aws-sdk/client-s3/package.json +2 -2
  23. package/node_modules/@aws-sdk/client-ses/dist-cjs/index.js +1 -1
  24. package/node_modules/@aws-sdk/client-ses/package.json +1 -1
  25. package/node_modules/@aws-sdk/client-ssm/dist-cjs/index.js +9 -3
  26. package/node_modules/@aws-sdk/client-ssm/dist-es/models/enums.js +4 -0
  27. package/node_modules/@aws-sdk/client-ssm/dist-es/schemas/schemas_0.js +3 -2
  28. package/node_modules/@aws-sdk/client-ssm/dist-types/commands/DeleteResourcePolicyCommand.d.ts +6 -0
  29. package/node_modules/@aws-sdk/client-ssm/dist-types/commands/PutResourcePolicyCommand.d.ts +12 -0
  30. package/node_modules/@aws-sdk/client-ssm/dist-types/models/enums.d.ts +12 -0
  31. package/node_modules/@aws-sdk/client-ssm/dist-types/models/models_0.d.ts +20 -1
  32. package/node_modules/@aws-sdk/client-ssm/dist-types/models/models_1.d.ts +2 -2
  33. package/node_modules/@aws-sdk/client-ssm/dist-types/ts3.4/models/enums.d.ts +5 -0
  34. package/node_modules/@aws-sdk/client-ssm/dist-types/ts3.4/models/models_0.d.ts +2 -0
  35. package/node_modules/@aws-sdk/client-ssm/package.json +1 -1
  36. package/node_modules/@zone-eu/mailsplit/lib/flowed-decoder.js +1 -5
  37. package/node_modules/@zone-eu/mailsplit/lib/headers.js +35 -25
  38. package/node_modules/@zone-eu/mailsplit/lib/message-splitter.js +126 -79
  39. package/node_modules/@zone-eu/mailsplit/lib/mime-node.js +27 -5
  40. package/node_modules/@zone-eu/mailsplit/lib/node-rewriter.js +3 -2
  41. package/node_modules/@zone-eu/mailsplit/lib/types.d.ts +0 -3
  42. package/node_modules/@zone-eu/mailsplit/package.json +5 -5
  43. package/node_modules/libbase64/.github/workflows/release.yaml +12 -10
  44. package/node_modules/libbase64/.ncurc.js +7 -1
  45. package/node_modules/libbase64/CHANGELOG.md +7 -0
  46. package/node_modules/libbase64/lib/libbase64.js +27 -0
  47. package/node_modules/libbase64/package.json +4 -4
  48. package/node_modules/libmime/CHANGELOG.md +14 -0
  49. package/node_modules/libmime/lib/libmime.js +41 -21
  50. package/node_modules/libmime/package.json +3 -3
  51. package/node_modules/libqp/.github/workflows/release.yaml +12 -10
  52. package/node_modules/libqp/CHANGELOG.md +7 -0
  53. package/node_modules/libqp/lib/libqp.js +6 -4
  54. package/node_modules/libqp/package.json +2 -2
  55. package/node_modules/libqp/test/libqp-test.js +71 -14
  56. package/node_modules/mailparser/.release-please-manifest.json +1 -1
  57. package/node_modules/mailparser/CHANGELOG.md +42 -0
  58. package/node_modules/mailparser/README.md +5 -0
  59. package/node_modules/mailparser/lib/mail-parser.js +107 -32
  60. package/node_modules/mailparser/lib/simple-parser.js +12 -0
  61. package/node_modules/mailparser/lib/stream-hash.js +1 -1
  62. package/node_modules/mailparser/package.json +6 -6
  63. package/node_modules/nodemailer/CHANGELOG.md +38 -0
  64. package/node_modules/nodemailer/README.md +1 -1
  65. package/node_modules/nodemailer/dist/cjs/addressparser/index.d.ts +16 -2
  66. package/node_modules/nodemailer/dist/cjs/addressparser/index.js +147 -28
  67. package/node_modules/nodemailer/dist/cjs/base64/index.js +2 -1
  68. package/node_modules/nodemailer/dist/cjs/dkim/index.d.ts +13 -1
  69. package/node_modules/nodemailer/dist/cjs/dkim/index.js +25 -8
  70. package/node_modules/nodemailer/dist/cjs/dkim/message-parser.d.ts +10 -1
  71. package/node_modules/nodemailer/dist/cjs/dkim/message-parser.js +28 -4
  72. package/node_modules/nodemailer/dist/cjs/dkim/relaxed-body.d.ts +10 -1
  73. package/node_modules/nodemailer/dist/cjs/dkim/sign.d.ts +3 -0
  74. package/node_modules/nodemailer/dist/cjs/dkim/sign.js +18 -1
  75. package/node_modules/nodemailer/dist/cjs/errors.d.ts +3 -2
  76. package/node_modules/nodemailer/dist/cjs/fetch/cookies.d.ts +12 -1
  77. package/node_modules/nodemailer/dist/cjs/fetch/index.d.ts +15 -1
  78. package/node_modules/nodemailer/dist/cjs/fetch/index.js +39 -64
  79. package/node_modules/nodemailer/dist/cjs/json-transport/index.d.ts +14 -4
  80. package/node_modules/nodemailer/dist/cjs/json-transport/index.js +2 -0
  81. package/node_modules/nodemailer/dist/cjs/mail-composer/index.d.ts +8 -3
  82. package/node_modules/nodemailer/dist/cjs/mailer/index.d.ts +43 -25
  83. package/node_modules/nodemailer/dist/cjs/mailer/mail-message.d.ts +11 -4
  84. package/node_modules/nodemailer/dist/cjs/mime-funcs/index.d.ts +7 -3
  85. package/node_modules/nodemailer/dist/cjs/mime-funcs/index.js +198 -84
  86. package/node_modules/nodemailer/dist/cjs/mime-node/index.d.ts +5 -2
  87. package/node_modules/nodemailer/dist/cjs/mime-node/index.js +2 -1
  88. package/node_modules/nodemailer/dist/cjs/nodemailer.d.ts +38 -16
  89. package/node_modules/nodemailer/dist/cjs/nodemailer.js +1 -0
  90. package/node_modules/nodemailer/dist/cjs/package-info.d.ts +1 -1
  91. package/node_modules/nodemailer/dist/cjs/package-info.js +1 -1
  92. package/node_modules/nodemailer/dist/cjs/punycode/index.js +1 -0
  93. package/node_modules/nodemailer/dist/cjs/qp/index.d.ts +2 -0
  94. package/node_modules/nodemailer/dist/cjs/qp/index.js +10 -1
  95. package/node_modules/nodemailer/dist/cjs/sendmail-transport/index.d.ts +2 -0
  96. package/node_modules/nodemailer/dist/cjs/ses-transport/index.d.ts +39 -2
  97. package/node_modules/nodemailer/dist/cjs/shared/index.d.ts +43 -7
  98. package/node_modules/nodemailer/dist/cjs/shared/index.js +5 -2
  99. package/node_modules/nodemailer/dist/cjs/smtp-connection/data-stream.d.ts +2 -1
  100. package/node_modules/nodemailer/dist/cjs/smtp-connection/data-stream.js +32 -24
  101. package/node_modules/nodemailer/dist/cjs/smtp-connection/http-proxy-client.d.ts +13 -5
  102. package/node_modules/nodemailer/dist/cjs/smtp-connection/http-proxy-client.js +23 -11
  103. package/node_modules/nodemailer/dist/cjs/smtp-connection/index.d.ts +59 -24
  104. package/node_modules/nodemailer/dist/cjs/smtp-connection/index.js +156 -75
  105. package/node_modules/nodemailer/dist/cjs/smtp-pool/index.d.ts +12 -4
  106. package/node_modules/nodemailer/dist/cjs/smtp-pool/index.js +39 -11
  107. package/node_modules/nodemailer/dist/cjs/smtp-pool/pool-resource.js +27 -31
  108. package/node_modules/nodemailer/dist/cjs/smtp-transport/index.d.ts +19 -6
  109. package/node_modules/nodemailer/dist/cjs/smtp-transport/index.js +25 -33
  110. package/node_modules/nodemailer/dist/cjs/stream-transport/index.d.ts +2 -0
  111. package/node_modules/nodemailer/dist/cjs/well-known/services.js +6 -0
  112. package/node_modules/nodemailer/dist/cjs/xoauth2/index.d.ts +13 -3
  113. package/node_modules/nodemailer/dist/cjs/xoauth2/index.js +25 -13
  114. package/node_modules/nodemailer/dist/esm/addressparser/index.d.ts +16 -2
  115. package/node_modules/nodemailer/dist/esm/addressparser/index.js +147 -27
  116. package/node_modules/nodemailer/dist/esm/base64/index.js +2 -1
  117. package/node_modules/nodemailer/dist/esm/dkim/index.d.ts +13 -1
  118. package/node_modules/nodemailer/dist/esm/dkim/index.js +25 -8
  119. package/node_modules/nodemailer/dist/esm/dkim/message-parser.d.ts +10 -1
  120. package/node_modules/nodemailer/dist/esm/dkim/message-parser.js +30 -5
  121. package/node_modules/nodemailer/dist/esm/dkim/relaxed-body.d.ts +10 -1
  122. package/node_modules/nodemailer/dist/esm/dkim/relaxed-body.js +2 -1
  123. package/node_modules/nodemailer/dist/esm/dkim/sign.d.ts +3 -0
  124. package/node_modules/nodemailer/dist/esm/dkim/sign.js +18 -1
  125. package/node_modules/nodemailer/dist/esm/errors.d.ts +3 -2
  126. package/node_modules/nodemailer/dist/esm/fetch/cookies.d.ts +12 -1
  127. package/node_modules/nodemailer/dist/esm/fetch/cookies.js +2 -1
  128. package/node_modules/nodemailer/dist/esm/fetch/index.d.ts +15 -1
  129. package/node_modules/nodemailer/dist/esm/fetch/index.js +39 -64
  130. package/node_modules/nodemailer/dist/esm/json-transport/index.d.ts +14 -4
  131. package/node_modules/nodemailer/dist/esm/json-transport/index.js +2 -0
  132. package/node_modules/nodemailer/dist/esm/mail-composer/index.d.ts +8 -3
  133. package/node_modules/nodemailer/dist/esm/mailer/index.d.ts +43 -25
  134. package/node_modules/nodemailer/dist/esm/mailer/mail-message.d.ts +11 -4
  135. package/node_modules/nodemailer/dist/esm/mime-funcs/index.d.ts +7 -3
  136. package/node_modules/nodemailer/dist/esm/mime-funcs/index.js +198 -84
  137. package/node_modules/nodemailer/dist/esm/mime-node/index.d.ts +5 -2
  138. package/node_modules/nodemailer/dist/esm/mime-node/index.js +2 -1
  139. package/node_modules/nodemailer/dist/esm/nodemailer.d.ts +38 -16
  140. package/node_modules/nodemailer/dist/esm/package-info.d.ts +1 -1
  141. package/node_modules/nodemailer/dist/esm/package-info.js +1 -1
  142. package/node_modules/nodemailer/dist/esm/punycode/index.js +1 -0
  143. package/node_modules/nodemailer/dist/esm/qp/index.d.ts +2 -0
  144. package/node_modules/nodemailer/dist/esm/qp/index.js +10 -1
  145. package/node_modules/nodemailer/dist/esm/sendmail-transport/index.d.ts +2 -0
  146. package/node_modules/nodemailer/dist/esm/ses-transport/index.d.ts +39 -2
  147. package/node_modules/nodemailer/dist/esm/shared/index.d.ts +43 -7
  148. package/node_modules/nodemailer/dist/esm/shared/index.js +5 -2
  149. package/node_modules/nodemailer/dist/esm/smtp-connection/data-stream.d.ts +2 -1
  150. package/node_modules/nodemailer/dist/esm/smtp-connection/data-stream.js +32 -24
  151. package/node_modules/nodemailer/dist/esm/smtp-connection/http-proxy-client.d.ts +13 -5
  152. package/node_modules/nodemailer/dist/esm/smtp-connection/http-proxy-client.js +23 -11
  153. package/node_modules/nodemailer/dist/esm/smtp-connection/index.d.ts +59 -24
  154. package/node_modules/nodemailer/dist/esm/smtp-connection/index.js +156 -75
  155. package/node_modules/nodemailer/dist/esm/smtp-pool/index.d.ts +12 -4
  156. package/node_modules/nodemailer/dist/esm/smtp-pool/index.js +39 -11
  157. package/node_modules/nodemailer/dist/esm/smtp-pool/pool-resource.js +27 -31
  158. package/node_modules/nodemailer/dist/esm/smtp-transport/index.d.ts +19 -6
  159. package/node_modules/nodemailer/dist/esm/smtp-transport/index.js +25 -33
  160. package/node_modules/nodemailer/dist/esm/stream-transport/index.d.ts +2 -0
  161. package/node_modules/nodemailer/dist/esm/well-known/services.js +6 -0
  162. package/node_modules/nodemailer/dist/esm/xoauth2/index.d.ts +13 -3
  163. package/node_modules/nodemailer/dist/esm/xoauth2/index.js +25 -13
  164. package/node_modules/nodemailer/dist/well-known/services.json +7 -0
  165. package/node_modules/nodemailer/package.json +16 -12
  166. package/package.json +16 -16
@@ -74,12 +74,14 @@ class XOAuth2 extends node_stream_1.Stream {
74
74
  constructor(options, logger) {
75
75
  super();
76
76
  this.options = options || {};
77
+ this.configError = false;
77
78
  if (options && options.serviceClient) {
78
79
  if (!options.privateKey || !options.user) {
80
+ // reported through getToken() rather than an 'error' event: the transports forward
81
+ // that event up to the Mail object, where nothing may be listening
79
82
  const err = new Error('Options "privateKey" and "user" are required for service account!');
80
83
  err.code = errors.EOAUTH2;
81
- setImmediate(() => this.emit('error', err));
82
- return;
84
+ this.configError = err;
83
85
  }
84
86
  const serviceRequestTimeout = Math.min(Math.max(Number(this.options.serviceRequestTimeout) || 0, 0), 3600);
85
87
  this.options.serviceRequestTimeout = serviceRequestTimeout || 5 * 60;
@@ -111,6 +113,11 @@ class XOAuth2 extends node_stream_1.Stream {
111
113
  * @param callback Callback function with error object and token string
112
114
  */
113
115
  getToken(renew, callback) {
116
+ // the error paths hand over the error alone
117
+ const done = callback;
118
+ if (this.configError) {
119
+ return done(this.configError);
120
+ }
114
121
  if (!renew && this.accessToken && (!this.expires || this.expires > Date.now())) {
115
122
  this.logger.debug({
116
123
  tnx: 'OAUTH2',
@@ -136,11 +143,11 @@ class XOAuth2 extends node_stream_1.Stream {
136
143
  }, 'Cannot renew access token for %s: No refresh mechanism available', this.options.user);
137
144
  const err = new Error("Can't create new access token for user");
138
145
  err.code = errors.EOAUTH2;
139
- return callback(err);
146
+ return done(err);
140
147
  }
141
148
  // If renewal already in progress, queue this request instead of starting another
142
149
  if (this.renewing) {
143
- this.renewalQueue.push({ renew, callback });
150
+ this.renewalQueue.push({ renew, callback: done });
144
151
  return;
145
152
  }
146
153
  this.renewing = true;
@@ -165,7 +172,7 @@ class XOAuth2 extends node_stream_1.Stream {
165
172
  }, 'Generated new Access Token for %s', this.options.user);
166
173
  }
167
174
  // Complete original request
168
- callback(err, accessToken);
175
+ done(err, accessToken);
169
176
  };
170
177
  if (this.provisionCallback) {
171
178
  this.provisionCallback(this.options.user, !!renew, (err, accessToken, expires) => {
@@ -204,6 +211,8 @@ class XOAuth2 extends node_stream_1.Stream {
204
211
  * @param callback Callback function with error object and token string
205
212
  */
206
213
  generateToken(callback) {
214
+ // the error paths hand over the error alone
215
+ const done = callback;
207
216
  let urlOptions;
208
217
  let loggedUrlOptions;
209
218
  if (this.options.serviceClient) {
@@ -224,7 +233,7 @@ class XOAuth2 extends node_stream_1.Stream {
224
233
  catch (_err) {
225
234
  const err = new Error("Can't generate token. Check your auth options");
226
235
  err.code = errors.EOAUTH2;
227
- return callback(err);
236
+ return done(err);
228
237
  }
229
238
  urlOptions = {
230
239
  grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
@@ -239,7 +248,7 @@ class XOAuth2 extends node_stream_1.Stream {
239
248
  if (!this.options.refreshToken) {
240
249
  const err = new Error("Can't create new access token for user");
241
250
  err.code = errors.EOAUTH2;
242
- return callback(err);
251
+ return done(err);
243
252
  }
244
253
  // web app - https://developers.google.com/identity/protocols/OAuth2WebServer
245
254
  urlOptions = {
@@ -265,13 +274,13 @@ class XOAuth2 extends node_stream_1.Stream {
265
274
  this.postRequest(this.options.accessUrl, urlOptions, this.options, (error, body) => {
266
275
  let data;
267
276
  if (error) {
268
- return callback(error);
277
+ return done(error);
269
278
  }
270
279
  try {
271
280
  data = JSON.parse(body.toString());
272
281
  }
273
282
  catch (E) {
274
- return callback(E);
283
+ return done(E);
275
284
  }
276
285
  if (!data || typeof data !== 'object') {
277
286
  this.logger.debug({
@@ -281,7 +290,7 @@ class XOAuth2 extends node_stream_1.Stream {
281
290
  }, 'Response: %s', (body || '').toString());
282
291
  const err = new Error('Invalid authentication response');
283
292
  err.code = errors.EOAUTH2;
284
- return callback(err);
293
+ return done(err);
285
294
  }
286
295
  const logData = Object.assign({}, data);
287
296
  if (logData.access_token) {
@@ -303,7 +312,7 @@ class XOAuth2 extends node_stream_1.Stream {
303
312
  }
304
313
  const err = new Error(errorMessage);
305
314
  err.code = errors.EOAUTH2;
306
- return callback(err);
315
+ return done(err);
307
316
  }
308
317
  if (data.access_token) {
309
318
  this.updateToken(data.access_token, data.expires_in);
@@ -311,7 +320,7 @@ class XOAuth2 extends node_stream_1.Stream {
311
320
  }
312
321
  const err = new Error('No access token');
313
322
  err.code = errors.EOAUTH2;
314
- return callback(err);
323
+ return done(err);
315
324
  });
316
325
  }
317
326
  /**
@@ -345,7 +354,10 @@ class XOAuth2 extends node_stream_1.Stream {
345
354
  method: 'post',
346
355
  headers: params.customHeaders,
347
356
  body: payload,
348
- allowErrorResponse: true
357
+ allowErrorResponse: true,
358
+ // unset falls back to the fetch default, a stalled token endpoint would otherwise keep
359
+ // `renewing` set and queue every later request
360
+ timeout: params.requestTimeout
349
361
  };
350
362
  // OAuth2 token endpoints are credential-bearing. src/fetch already
351
363
  // validates certs by default; pin rejectUnauthorized:true here so the
@@ -39,7 +39,21 @@ export type Address = MailboxAddress | GroupAddress;
39
39
  *
40
40
  * @param str Address field
41
41
  * @param options Optional options object
42
- * @param options._depth Internal recursion depth counter (do not set manually)
43
42
  * @return An array of address objects
44
43
  */
45
- export default function addressparser(str?: string | null, options?: AddressParserOptions): Address[];
44
+ declare function addressparser(str: string | null | undefined, options: AddressParserOptions & {
45
+ flatten: true;
46
+ }): MailboxAddress[];
47
+ declare function addressparser(str?: string | null, options?: AddressParserOptions): Address[];
48
+ /**
49
+ * Type aliases in the layout of @types/nodemailer, so `addressparser.Address` style references keep working
50
+ */
51
+ type AddressEntry = Address;
52
+ declare namespace addressparser {
53
+ type Address = MailboxAddress;
54
+ type Group = GroupAddress;
55
+ type AddressOrGroup = AddressEntry;
56
+ }
57
+ /** The names @types/nodemailer used for the group entry and for the union of both entry types */
58
+ export type { GroupAddress as Group, Address as AddressOrGroup };
59
+ export default addressparser;
@@ -240,6 +240,81 @@ function _recoverAddrSpec(data) {
240
240
  .filter(part => part)
241
241
  .join(' ');
242
242
  }
243
+ /**
244
+ * Takes the RFC 5322 comments out of the contents of an angle-addr.
245
+ *
246
+ * The tokenizer tracks a single open operator, so once '<' is open a '(' is plain text and
247
+ * a comment inside the brackets reached the address verbatim: 'Name <user@example.com(x)evil.com>'
248
+ * put 'user@example.com(x)evil.com' into the envelope, a value that is no mailbox and that a
249
+ * receiver stripping the comment reads as 'user@example.comevil.com' (GHSA-g73g-hqqh-jr95).
250
+ * A comment is folding whitespace, so it is read as a space that splits two atoms and as
251
+ * nothing next to an '@', the same rule the token walk applies outside the brackets. Quoted
252
+ * strings and domain literals are copied through untouched, a '(' in there is not a comment.
253
+ *
254
+ * @param address Contents of the angle brackets
255
+ * @return The address with the comments removed, and the text of the comments
256
+ */
257
+ function _stripAddressComments(address) {
258
+ const comments = [];
259
+ let result = '';
260
+ let comment = '';
261
+ let depth = 0;
262
+ let closer = '';
263
+ // carried along rather than read back off the growing result, which would flatten it on
264
+ // every comment (see lastChars in _handleAddress)
265
+ let lastChar = '';
266
+ for (let i = 0, len = address.length; i < len; i++) {
267
+ const chr = address.charAt(i);
268
+ if (depth) {
269
+ if (chr === '\\' && i < len - 1) {
270
+ comment += address.charAt(++i);
271
+ }
272
+ else if (chr === '(') {
273
+ depth++;
274
+ comment += chr;
275
+ }
276
+ else if (chr === ')' && !--depth) {
277
+ comments.push(comment.trim());
278
+ comment = '';
279
+ if (lastChar !== '@' && address.charAt(i + 1) !== '@') {
280
+ result += ' ';
281
+ lastChar = ' ';
282
+ }
283
+ }
284
+ else {
285
+ comment += chr;
286
+ }
287
+ continue;
288
+ }
289
+ if (closer) {
290
+ if (chr === '\\' && closer === '"' && i < len - 1) {
291
+ result += chr + address.charAt(++i);
292
+ lastChar = address.charAt(i);
293
+ continue;
294
+ }
295
+ if (chr === closer) {
296
+ closer = '';
297
+ }
298
+ }
299
+ else if (chr === '"') {
300
+ closer = '"';
301
+ }
302
+ else if (chr === '[') {
303
+ closer = ']';
304
+ }
305
+ else if (chr === '(') {
306
+ depth = 1;
307
+ continue;
308
+ }
309
+ result += chr;
310
+ lastChar = chr;
311
+ }
312
+ if (depth) {
313
+ // an unterminated comment runs to the end of the address
314
+ comments.push(comment.trim());
315
+ }
316
+ return { address: result.trim(), comments: comments.filter(text => text) };
317
+ }
243
318
  /**
244
319
  * Converts tokens for a single address into an address object
245
320
  *
@@ -351,7 +426,7 @@ function _handleAddress(tokens, depth) {
351
426
  // Parse group members, but flatten any nested groups (RFC 5322 doesn't allow nesting)
352
427
  let groupMembers = [];
353
428
  if (data.group.length) {
354
- const parsedGroup = addressparser(data.group.join(','), { _depth: depth + 1 });
429
+ const parsedGroup = _parseAddressList(data.group.join(','), depth + 1);
355
430
  parsedGroup.forEach(member => {
356
431
  if (member.group) {
357
432
  groupMembers = groupMembers.concat(member.group);
@@ -367,6 +442,24 @@ function _handleAddress(tokens, depth) {
367
442
  });
368
443
  }
369
444
  else {
445
+ // Comments come out of the angle-addr before anything asks whether one was found, so
446
+ // that brackets holding nothing but a comment read the same as empty ones
447
+ const addressComments = [];
448
+ const addressParts = [];
449
+ for (const part of data.address) {
450
+ if (part.indexOf('(') < 0) {
451
+ addressParts.push(part);
452
+ continue;
453
+ }
454
+ const stripped = _stripAddressComments(part);
455
+ for (const comment of stripped.comments) {
456
+ addressComments.push(comment);
457
+ }
458
+ if (stripped.address) {
459
+ addressParts.push(stripped.address);
460
+ }
461
+ }
462
+ data.address = addressParts;
370
463
  // If no address was found, try to detect one from regular text
371
464
  if (!data.address.length && data.text.length) {
372
465
  for (let i = data.text.length - 1; i >= 0; i--) {
@@ -420,7 +513,13 @@ function _handleAddress(tokens, depth) {
420
513
  // Join values with spaces
421
514
  data.text = data.text.join(' ');
422
515
  data.address = data.address.join(' ');
423
- if (addressFromQuotedText && data.text) {
516
+ if (addressFromQuotedText && data.text.indexOf('@') >= 0) {
517
+ // A quoted run with no '@' in it is a display name and nothing else, so it stays
518
+ // where it is. Moving it over anyway made a comment the mailbox was read from:
519
+ // '"Display Name" <(a comment)>' handed on 'Display Name' as the address with the
520
+ // comment as the name, and a quoted name holding a ',' or a ';' became an address
521
+ // that reads as two recipients once a consumer writes it back into a header.
522
+ //
424
523
  // The mailbox is still sitting in the text, so it moves over here and is quoted
425
524
  // before the recovery below rather than after it. Anything else the text holds
426
525
  // came along with it: a comment ends the domain but leaves the atoms behind it in
@@ -433,6 +532,10 @@ function _handleAddress(tokens, depth) {
433
532
  data.text = '';
434
533
  }
435
534
  _recoverAddrSpec(data);
535
+ // a comment names the mailbox only when nothing else does, as one outside the brackets
536
+ if (!data.text && addressComments.length) {
537
+ data.text = addressComments.join(' ');
538
+ }
436
539
  const address = {
437
540
  address: data.address || data.text || '',
438
541
  name: data.text || data.address || ''
@@ -463,6 +566,7 @@ class Tokenizer {
463
566
  this.node = null;
464
567
  this.escaped = false;
465
568
  this.inDomainLiteral = false;
569
+ this.afterAt = false;
466
570
  this.list = [];
467
571
  /**
468
572
  * Operator tokens and which tokens are expected to end the sequence
@@ -506,17 +610,23 @@ class Tokenizer {
506
610
  * Checks if a character is an operator or text and acts accordingly
507
611
  *
508
612
  * @param chr Character from the address field
613
+ * @param nextChr Character following chr, null at the end of the field
509
614
  */
510
615
  checkChar(chr, nextChr) {
511
- // Track RFC 5322 domain-literals ("[" *dtext "]"). Operator characters such
512
- // as the ":" of an IPv6 address-literal (user@[IPv6:2001:db8::1]) are dtext
513
- // and must not be treated as the group delimiter while inside the brackets.
514
- // Quoted strings and comments are handled separately via operatorExpecting,
515
- // so only enter this state when no operator is open. The list separators ","
516
- // and ";" are the exception: they always end the literal (and split the
517
- // address list) so that an unclosed "[" cannot swallow later recipients.
616
+ // Track RFC 5322 domain-literals ("[" *dtext "]"). The ":" of an IPv6
617
+ // address-literal (user@[IPv6:2001:db8::1]) is dtext and must not be treated as
618
+ // the group delimiter while inside the brackets. That is the only operator the
619
+ // literal hides: letting it hide the others as well meant a "[" could turn the
620
+ // comment, quoted string or angle-addr after it into text and pick a different
621
+ // mailbox, '[ ( ] <a@victim.com> ) <b@evil.com>' yielded a@victim.com where
622
+ // RFC 5322 reads b@evil.com. A "[" also only opens a literal right after the "@"
623
+ // of an addr-spec, so the group delimiter of a display name holding one stays
624
+ // intact. Quoted strings and comments are handled separately via
625
+ // operatorExpecting, so only enter this state when no operator is open. The list
626
+ // separators "," and ";" always end the literal (and split the address list) so
627
+ // that an unclosed "[" cannot swallow later recipients.
518
628
  if (!this.escaped && !this.operatorExpecting) {
519
- if (!this.inDomainLiteral && chr === '[') {
629
+ if (!this.inDomainLiteral && chr === '[' && this.afterAt) {
520
630
  this.inDomainLiteral = true;
521
631
  }
522
632
  else if (this.inDomainLiteral && (chr === ']' || chr === ',' || chr === ';')) {
@@ -536,17 +646,24 @@ class Tokenizer {
536
646
  }
537
647
  this.list.push(this.node);
538
648
  this.node = null;
649
+ if (chr !== ')') {
650
+ // a comment is folding whitespace, it does not part the "@" from the "["
651
+ this.afterAt = false;
652
+ }
539
653
  this.operatorExpecting = '';
540
654
  this.escaped = false;
541
655
  return;
542
656
  }
543
- else if (!this.operatorExpecting && !this.inDomainLiteral && chr in this.operators) {
657
+ else if (!this.operatorExpecting && !(this.inDomainLiteral && chr === ':') && chr in this.operators) {
544
658
  this.node = {
545
659
  type: 'operator',
546
660
  value: chr
547
661
  };
548
662
  this.list.push(this.node);
549
663
  this.node = null;
664
+ if (chr !== '(') {
665
+ this.afterAt = false;
666
+ }
550
667
  this.operatorExpecting = this.operators[chr];
551
668
  this.escaped = false;
552
669
  return;
@@ -570,6 +687,11 @@ class Tokenizer {
570
687
  if (chr.charCodeAt(0) >= 0x21 || [' ', '\t'].includes(chr)) {
571
688
  // skip command bytes
572
689
  this.node.value += chr;
690
+ // text inside a quoted string or a comment is not part of an addr-spec. Tracked
691
+ // as it goes rather than read back off the value (see lastChars in _handleAddress)
692
+ if (!this.operatorExpecting && chr !== ' ' && chr !== '\t') {
693
+ this.afterAt = chr === '@';
694
+ }
573
695
  }
574
696
  this.escaped = false;
575
697
  }
@@ -581,24 +703,18 @@ class Tokenizer {
581
703
  */
582
704
  const MAX_NESTED_GROUP_DEPTH = 50;
583
705
  /**
584
- * Parses structured e-mail addresses from an address field
585
- *
586
- * Example:
706
+ * Parses an address list, recursing into the groups it holds.
587
707
  *
588
- * 'Name <address@domain>'
589
- *
590
- * will be converted to
591
- *
592
- * [{name: 'Name', address: 'address@domain'}]
708
+ * The depth is threaded through the calls rather than carried in an options object, so a
709
+ * caller supplied one can not seed it and lift the recursion limit: `{ _depth: -1e9 }`
710
+ * bought a billion levels of nesting and turned the guard below into the stack overflow it
711
+ * is there to prevent.
593
712
  *
594
713
  * @param str Address field
595
- * @param options Optional options object
596
- * @param options._depth Internal recursion depth counter (do not set manually)
714
+ * @param depth Current recursion depth for nested group protection
597
715
  * @return An array of address objects
598
716
  */
599
- export default function addressparser(str, options) {
600
- options = options || {};
601
- const depth = options._depth || 0;
717
+ function _parseAddressList(str, depth) {
602
718
  // Prevent stack overflow from deeply nested groups (DoS protection)
603
719
  if (depth > MAX_NESTED_GROUP_DEPTH) {
604
720
  return [];
@@ -607,7 +723,7 @@ export default function addressparser(str, options) {
607
723
  const tokens = tokenizer.tokenize();
608
724
  const addresses = [];
609
725
  let address = [];
610
- let parsedAddresses = [];
726
+ const parsedAddresses = [];
611
727
  tokens.forEach(token => {
612
728
  if (token.type === 'operator' && (token.value === ',' || token.value === ';')) {
613
729
  if (address.length) {
@@ -648,8 +764,11 @@ export default function addressparser(str, options) {
648
764
  }
649
765
  }
650
766
  mergedAddresses.reverse();
651
- parsedAddresses = mergedAddresses;
652
- if (options.flatten) {
767
+ return mergedAddresses;
768
+ }
769
+ function addressparser(str, options) {
770
+ const parsedAddresses = _parseAddressList(str, 0);
771
+ if (options?.flatten) {
653
772
  const flatAddresses = [];
654
773
  const walkAddressList = (list) => {
655
774
  list.forEach(entry => {
@@ -664,3 +783,4 @@ export default function addressparser(str, options) {
664
783
  }
665
784
  return parsedAddresses;
666
785
  }
786
+ export default addressparser;
@@ -20,7 +20,8 @@ export function encode(buffer) {
20
20
  */
21
21
  export function wrap(str, lineLength) {
22
22
  str = (str || '').toString();
23
- lineLength = lineLength || 76;
23
+ // a negative length would step backwards through the input and never finish
24
+ lineLength = Math.max(Number(lineLength) || 76, 1);
24
25
  if (str.length <= lineLength) {
25
26
  return str;
26
27
  }
@@ -30,11 +30,23 @@ declare class DKIM {
30
30
  constructor(options: DKIMOptions);
31
31
  sign(input: Readable | Buffer | string, extraOptions?: DKIMOptions): DKIMSignedStream;
32
32
  }
33
+ /** The signer options without any key material */
34
+ export type DKIMOptionalOptions = Omit<DKIMOptions, 'keys' | 'domainName' | 'keySelector' | 'privateKey'>;
35
+ /** The signer options for a single key given as domainName, keySelector and privateKey */
36
+ export type DKIMSingleKeyOptions = Omit<DKIMOptions, 'keys'>;
37
+ /** The signer options for one or more keys given through `keys` */
38
+ export type DKIMMultipleKeysOptions = DKIMOptionalOptions & {
39
+ keys: DKIMKey | DKIMKey[];
40
+ };
33
41
  /**
34
42
  * Type aliases in the layout of @types/nodemailer, so `DKIM.Options` style references keep working
35
43
  */
36
44
  declare namespace DKIM {
37
45
  type Options = DKIMOptions;
38
- type SingleKeyOptions = Omit<DKIMOptions, 'keys'>;
46
+ type OptionalOptions = DKIMOptionalOptions;
47
+ type SingleKeyOptions = DKIMSingleKeyOptions;
48
+ type MultipleKeysOptions = DKIMMultipleKeysOptions;
39
49
  }
50
+ /** The same aliases as module level exports, for `import * as DKIM` and `import DKIM = require()` */
51
+ export type { DKIMOptions as Options, DKIMOptionalOptions as OptionalOptions, DKIMSingleKeyOptions as SingleKeyOptions, DKIMMultipleKeysOptions as MultipleKeysOptions };
40
52
  export default DKIM;
@@ -85,13 +85,22 @@ class DKIMSigner {
85
85
  return;
86
86
  }
87
87
  const key = this.keys[keyPos++];
88
- const dkimField = sign(this.headers, this.hashAlgo, this.bodyHash, {
89
- domainName: key.domainName,
90
- keySelector: key.keySelector,
91
- privateKey: key.privateKey,
92
- headerFieldNames: this.options.headerFieldNames,
93
- skipFields: this.options.skipFields
94
- });
88
+ let dkimField;
89
+ try {
90
+ dkimField = sign(this.headers, this.hashAlgo, this.bodyHash, {
91
+ domainName: key.domainName,
92
+ keySelector: key.keySelector,
93
+ privateKey: key.privateKey,
94
+ headerFieldNames: this.options.headerFieldNames,
95
+ skipFields: this.options.skipFields
96
+ });
97
+ }
98
+ catch (err) {
99
+ this.hasErrored = true;
100
+ this.cleanup();
101
+ this.output.emit('error', err);
102
+ return;
103
+ }
95
104
  if (dkimField) {
96
105
  this.output.write(Buffer.from(dkimField + '\r\n'));
97
106
  }
@@ -191,7 +200,15 @@ class DKIM {
191
200
  }
192
201
  const signer = new DKIMSigner(options, this.keys, inputStream, output);
193
202
  setImmediate(() => {
194
- signer.signStream();
203
+ try {
204
+ signer.signStream();
205
+ }
206
+ catch (_E) {
207
+ // the body hash is created here, an unknown hashAlgo throws inside this timer
208
+ // where nothing else could catch it
209
+ output.emit('error', sign.unsupportedHashAlgoError(signer.hashAlgo));
210
+ return;
211
+ }
195
212
  if (writeValue) {
196
213
  setImmediate(() => {
197
214
  inputStream.end(writeValue);
@@ -13,7 +13,7 @@ export interface MessageParserHeaderLine {
13
13
  * from the rest of the body. Headers are emitted with the 'headers' event. Message
14
14
  * body is passed on as the resulting stream.
15
15
  */
16
- export default class MessageParser extends Transform {
16
+ declare class MessageParser extends Transform {
17
17
  lastBytes: Buffer;
18
18
  headersParsed: boolean;
19
19
  headerBytes: number;
@@ -37,3 +37,12 @@ export default class MessageParser extends Transform {
37
37
  checkHeaders(data: Buffer): boolean;
38
38
  parseHeaders(): MessageParserHeaderLine[];
39
39
  }
40
+ /**
41
+ * Type aliases in the layout of @types/nodemailer, so `MessageParser.Header` style references keep working
42
+ */
43
+ declare namespace MessageParser {
44
+ type Header = MessageParserHeaderLine;
45
+ }
46
+ /** The same alias as a module level export, for `import * as MessageParser` and `import MessageParser = require()` */
47
+ export type { MessageParserHeaderLine as Header };
48
+ export default MessageParser;
@@ -1,10 +1,37 @@
1
1
  import { Transform } from 'node:stream';
2
+ /**
3
+ * Drops the SP and HTAB from both ends of a field name.
4
+ *
5
+ * An index scan rather than `/^[ \t]+|[ \t]+$/g`, which retries the trailing branch at
6
+ * every offset of a blank run that is followed by other text: the run is consumed greedily,
7
+ * `$` fails, and the engine gives the characters back one at a time before moving on to
8
+ * start the same walk one offset further in. A field name carrying a 128 KiB run of spaces
9
+ * between two atoms took about 6.7 seconds to trim, and the cost grows with its square.
10
+ *
11
+ * Not `.trim()`, which takes the other Unicode spaces along: an NBSP in front of a field
12
+ * name would come off and select the name into the signed set, where the relaxed
13
+ * canonicalization in sign.ts deliberately leaves everything but SP and HTAB alone.
14
+ *
15
+ * @param str Field name to trim
16
+ * @return The field name without the surrounding SP and HTAB
17
+ */
18
+ function _trimFieldName(str) {
19
+ let start = 0;
20
+ let end = str.length;
21
+ while (start < end && (str.charCodeAt(start) === 0x20 || str.charCodeAt(start) === 0x09)) {
22
+ start++;
23
+ }
24
+ while (end > start && (str.charCodeAt(end - 1) === 0x20 || str.charCodeAt(end - 1) === 0x09)) {
25
+ end--;
26
+ }
27
+ return str.slice(start, end);
28
+ }
2
29
  /**
3
30
  * MessageParser instance is a transform stream that separates message headers
4
31
  * from the rest of the body. Headers are emitted with the 'headers' event. Message
5
32
  * body is passed on as the resulting stream.
6
33
  */
7
- export default class MessageParser extends Transform {
34
+ class MessageParser extends Transform {
8
35
  constructor(options) {
9
36
  super(options);
10
37
  this.lastBytes = Buffer.alloc(4);
@@ -141,11 +168,9 @@ export default class MessageParser extends Transform {
141
168
  return lines
142
169
  .filter(line => /[^ \t\r]/.test(line))
143
170
  .map(line => ({
144
- key: line
145
- .substr(0, line.indexOf(':'))
146
- .replace(/^[ \t]+|[ \t]+$/g, '')
147
- .toLowerCase(),
171
+ key: _trimFieldName(line.substr(0, line.indexOf(':'))).toLowerCase(),
148
172
  line
149
173
  }));
150
174
  }
151
175
  }
176
+ export default MessageParser;
@@ -17,7 +17,7 @@ export interface RelaxedBodyOptions {
17
17
  * a non-empty body always ends with CRLF. Bytes are canonicalized as they arrive,
18
18
  * so a line of any length costs constant memory.
19
19
  */
20
- export default class RelaxedBody extends Transform {
20
+ declare class RelaxedBody extends Transform {
21
21
  bodyHash: crypto.Hash;
22
22
  /** Bytes of the original body seen so far */
23
23
  byteLength: number;
@@ -25,3 +25,12 @@ export default class RelaxedBody extends Transform {
25
25
  constructor(options?: RelaxedBodyOptions);
26
26
  updateHash(chunk: Buffer, final?: boolean): void;
27
27
  }
28
+ /**
29
+ * Type aliases in the layout of @types/nodemailer, so `RelaxedBody.Options` style references keep working
30
+ */
31
+ declare namespace RelaxedBody {
32
+ type Options = RelaxedBodyOptions;
33
+ }
34
+ /** The same alias as a module level export, for `import * as RelaxedBody` and `import RelaxedBody = require()` */
35
+ export type { RelaxedBodyOptions as Options };
36
+ export default RelaxedBody;
@@ -16,7 +16,7 @@ const EMPTY_LINES = Buffer.alloc(4096, CRLF);
16
16
  * a non-empty body always ends with CRLF. Bytes are canonicalized as they arrive,
17
17
  * so a line of any length costs constant memory.
18
18
  */
19
- export default class RelaxedBody extends Transform {
19
+ class RelaxedBody extends Transform {
20
20
  constructor(options) {
21
21
  super();
22
22
  options = options || {};
@@ -138,3 +138,4 @@ export default class RelaxedBody extends Transform {
138
138
  callback();
139
139
  }
140
140
  }
141
+ export default RelaxedBody;
@@ -1,4 +1,5 @@
1
1
  import crypto from 'node:crypto';
2
+ import type { NodemailerError } from '../errors.js';
2
3
  import type { MessageParserHeaderLine } from './message-parser.js';
3
4
  /**
4
5
  * Private key accepted by crypto.Sign#sign: a PEM string, a Buffer, a KeyObject or an
@@ -35,6 +36,7 @@ export interface DKIMRelaxedHeaders {
35
36
  * Returns DKIM signature header line
36
37
  *
37
38
  * @param headers Parsed headers object from MessageParser
39
+ * @param hashAlgo Hash algorithm the body hash was calculated with, for example "sha256"
38
40
  * @param bodyHash Base64 encoded hash of the message
39
41
  * @param options DKIM options
40
42
  * @param options.domainName Domain name to be signed for
@@ -45,5 +47,6 @@ export interface DKIMRelaxedHeaders {
45
47
  declare function sign(headers: MessageParserHeaderLine[], hashAlgo: string, bodyHash: string, options?: DKIMSignOptions): string | false;
46
48
  declare namespace sign {
47
49
  var relaxedHeaders: (headers: MessageParserHeaderLine[], fieldNames?: string, skipFields?: string) => DKIMRelaxedHeaders;
50
+ var unsupportedHashAlgoError: (hashAlgo: string) => NodemailerError;
48
51
  }
49
52
  export default sign;