@nocobase/plugin-workflow-mailer 2.2.17 → 2.2.18

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 (53) hide show
  1. package/dist/externalVersion.js +6 -6
  2. package/dist/node_modules/joi/dist/joi-browser.min.js +1 -1
  3. package/dist/node_modules/joi/lib/index.js +1 -1
  4. package/dist/node_modules/joi/lib/messages.js +35 -6
  5. package/dist/node_modules/joi/lib/types/keys.js +18 -1
  6. package/dist/node_modules/joi/lib/types/link.js +18 -2
  7. package/dist/node_modules/joi/lib/types/string.js +2 -2
  8. package/dist/node_modules/joi/package.json +1 -1
  9. package/dist/node_modules/nodemailer/.ncurc.js +2 -0
  10. package/dist/node_modules/nodemailer/.prettierignore +8 -0
  11. package/dist/node_modules/nodemailer/.prettierrc +12 -0
  12. package/dist/node_modules/nodemailer/.prettierrc.js +2 -0
  13. package/dist/node_modules/nodemailer/.release-please-config.json +9 -0
  14. package/dist/node_modules/nodemailer/eslint.config.js +88 -0
  15. package/dist/node_modules/nodemailer/lib/addressparser/index.js +314 -71
  16. package/dist/node_modules/nodemailer/lib/base64/index.js +9 -13
  17. package/dist/node_modules/nodemailer/lib/dkim/index.js +17 -18
  18. package/dist/node_modules/nodemailer/lib/dkim/message-parser.js +12 -13
  19. package/dist/node_modules/nodemailer/lib/dkim/relaxed-body.js +3 -3
  20. package/dist/node_modules/nodemailer/lib/dkim/sign.js +22 -18
  21. package/dist/node_modules/nodemailer/lib/errors.js +59 -0
  22. package/dist/node_modules/nodemailer/lib/fetch/cookies.js +14 -19
  23. package/dist/node_modules/nodemailer/lib/fetch/index.js +164 -27
  24. package/dist/node_modules/nodemailer/lib/json-transport/index.js +4 -4
  25. package/dist/node_modules/nodemailer/lib/mail-composer/index.js +229 -155
  26. package/dist/node_modules/nodemailer/lib/mailer/index.js +106 -54
  27. package/dist/node_modules/nodemailer/lib/mailer/mail-message.js +106 -68
  28. package/dist/node_modules/nodemailer/lib/mime-funcs/index.js +105 -59
  29. package/dist/node_modules/nodemailer/lib/mime-funcs/mime-types.js +21 -14
  30. package/dist/node_modules/nodemailer/lib/mime-node/index.js +389 -137
  31. package/dist/node_modules/nodemailer/lib/mime-node/last-newline.js +1 -1
  32. package/dist/node_modules/nodemailer/lib/mime-node/le-unix.js +4 -7
  33. package/dist/node_modules/nodemailer/lib/mime-node/le-windows.js +1 -4
  34. package/dist/node_modules/nodemailer/lib/nodemailer.js +1 -1
  35. package/dist/node_modules/nodemailer/lib/punycode/index.js +1 -1
  36. package/dist/node_modules/nodemailer/lib/qp/index.js +33 -22
  37. package/dist/node_modules/nodemailer/lib/sendmail-transport/index.js +54 -44
  38. package/dist/node_modules/nodemailer/lib/ses-transport/index.js +110 -193
  39. package/dist/node_modules/nodemailer/lib/shared/index.js +299 -240
  40. package/dist/node_modules/nodemailer/lib/shared/objects.js +42 -0
  41. package/dist/node_modules/nodemailer/lib/shared/url.js +151 -0
  42. package/dist/node_modules/nodemailer/lib/smtp-connection/data-stream.js +3 -6
  43. package/dist/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js +50 -19
  44. package/dist/node_modules/nodemailer/lib/smtp-connection/index.js +415 -223
  45. package/dist/node_modules/nodemailer/lib/smtp-pool/index.js +27 -34
  46. package/dist/node_modules/nodemailer/lib/smtp-pool/pool-resource.js +19 -16
  47. package/dist/node_modules/nodemailer/lib/smtp-transport/index.js +66 -53
  48. package/dist/node_modules/nodemailer/lib/stream-transport/index.js +16 -7
  49. package/dist/node_modules/nodemailer/lib/well-known/index.js +7 -7
  50. package/dist/node_modules/nodemailer/lib/well-known/services.json +328 -38
  51. package/dist/node_modules/nodemailer/lib/xoauth2/index.js +105 -33
  52. package/dist/node_modules/nodemailer/package.json +1 -1
  53. package/package.json +4 -4
@@ -4,7 +4,7 @@
4
4
 
5
5
  const MimeNode = require('../mime-node');
6
6
  const mimeFuncs = require('../mime-funcs');
7
- const parseDataURI = require('../shared').parseDataURI;
7
+ const { parseDataURI, copyOwnKeys } = require('../shared');
8
8
 
9
9
  /**
10
10
  * Creates the object for composing a MimeNode instance out from the mail options
@@ -32,7 +32,11 @@ class MailComposer {
32
32
 
33
33
  // Compose MIME tree
34
34
  if (this.mail.raw) {
35
- this.message = new MimeNode('message/rfc822', { newline: this.mail.newline }).setRaw(this.mail.raw);
35
+ this.message = new MimeNode('message/rfc822', {
36
+ newline: this.mail.newline,
37
+ disableUrlAccess: this.mail.disableUrlAccess,
38
+ disableFileAccess: this.mail.disableFileAccess
39
+ }).setRaw(this.mail.raw);
36
40
  } else if (this._useMixed) {
37
41
  this.message = this._createMixed();
38
42
  } else if (this._useAlternative) {
@@ -59,7 +63,7 @@ class MailComposer {
59
63
 
60
64
  // Add headers to the root node, always overrides custom headers
61
65
  ['from', 'sender', 'to', 'cc', 'bcc', 'reply-to', 'in-reply-to', 'references', 'subject', 'message-id', 'date'].forEach(header => {
62
- let key = header.replace(/-(\w)/g, (o, c) => c.toUpperCase());
66
+ const key = header.replace(/-(\w)/g, (o, c) => c.toUpperCase());
63
67
  if (this.mail[key]) {
64
68
  this.message.setHeader(header, this.mail[key]);
65
69
  }
@@ -83,23 +87,36 @@ class MailComposer {
83
87
  * @returns {Object} An object of arrays (`related` and `attached`)
84
88
  */
85
89
  getAttachments(findRelated) {
86
- let icalEvent, eventObject;
87
- let attachments = [].concat(this.mail.attachments || []).map((attachment, i) => {
88
- let data;
89
- let isMessageNode = /^message\//i.test(attachment.contentType);
90
-
90
+ let eventObject;
91
+ const attachments = [].concat(this.mail.attachments || []).map((attachment, i) => {
91
92
  if (/^data:/i.test(attachment.path || attachment.href)) {
92
93
  attachment = this._processDataUrl(attachment);
93
94
  }
94
95
 
95
- let contentType = attachment.contentType || mimeFuncs.detectMimeType(attachment.filename || attachment.path || attachment.href || 'bin');
96
- let isImage = /^image\//i.test(contentType);
97
- let contentDisposition = attachment.contentDisposition || (isMessageNode || (isImage && attachment.cid) ? 'inline' : 'attachment');
96
+ const contentType =
97
+ attachment.contentType || mimeFuncs.detectMimeType(attachment.filename || attachment.path || attachment.href || 'bin');
98
+
99
+ const isImage = /^image\//i.test(contentType);
100
+ const isMessageNode = /^message\//i.test(contentType);
101
+
102
+ const contentDisposition =
103
+ attachment.contentDisposition || (isMessageNode || (isImage && attachment.cid) ? 'inline' : 'attachment');
98
104
 
99
- data = {
105
+ let contentTransferEncoding;
106
+ if ('contentTransferEncoding' in attachment) {
107
+ // also contains `false`, to set
108
+ contentTransferEncoding = attachment.contentTransferEncoding;
109
+ } else if (isMessageNode) {
110
+ // the content might include non-ASCII bytes but at this point we do not know it yet
111
+ contentTransferEncoding = '8bit';
112
+ } else {
113
+ contentTransferEncoding = 'base64'; // the default
114
+ }
115
+
116
+ const data = {
100
117
  contentType,
101
118
  contentDisposition,
102
- contentTransferEncoding: 'contentTransferEncoding' in attachment ? attachment.contentTransferEncoding : 'base64'
119
+ contentTransferEncoding
103
120
  };
104
121
 
105
122
  if (attachment.filename) {
@@ -129,7 +146,8 @@ class MailComposer {
129
146
  } else if (attachment.href) {
130
147
  data.content = {
131
148
  href: attachment.href,
132
- httpHeaders: attachment.httpHeaders
149
+ httpHeaders: attachment.httpHeaders,
150
+ tls: attachment.tls
133
151
  };
134
152
  } else {
135
153
  data.content = attachment.content || '';
@@ -147,21 +165,7 @@ class MailComposer {
147
165
  });
148
166
 
149
167
  if (this.mail.icalEvent) {
150
- if (
151
- typeof this.mail.icalEvent === 'object' &&
152
- (this.mail.icalEvent.content || this.mail.icalEvent.path || this.mail.icalEvent.href || this.mail.icalEvent.raw)
153
- ) {
154
- icalEvent = this.mail.icalEvent;
155
- } else {
156
- icalEvent = {
157
- content: this.mail.icalEvent
158
- };
159
- }
160
-
161
- eventObject = {};
162
- Object.keys(icalEvent).forEach(key => {
163
- eventObject[key] = icalEvent[key];
164
- });
168
+ eventObject = Object.assign({}, this._getIcalEvent());
165
169
 
166
170
  eventObject.contentType = 'application/ics';
167
171
  if (!eventObject.headers) {
@@ -177,12 +181,75 @@ class MailComposer {
177
181
  attached: attachments.concat(eventObject || []),
178
182
  related: []
179
183
  };
180
- } else {
181
- return {
182
- attached: attachments.filter(attachment => !attachment.cid).concat(eventObject || []),
183
- related: attachments.filter(attachment => !!attachment.cid)
184
- };
185
184
  }
185
+
186
+ return {
187
+ attached: attachments.filter(attachment => !attachment.cid).concat(eventObject || []),
188
+ related: attachments.filter(attachment => !!attachment.cid)
189
+ };
190
+ }
191
+
192
+ /**
193
+ * Returns the icalEvent value with `path`/`href`/data uri input normalized into
194
+ * a `content` entry, the same way as for regular attachments. The same event is
195
+ * included twice (as a text/calendar alternative and as an application/ics
196
+ * attachment), so the shared content object is marked to be resolved just once
197
+ * and the buffered result is reused by the second node.
198
+ *
199
+ * @returns {Object} Normalized icalEvent data
200
+ */
201
+ _getIcalEvent() {
202
+ if (!this._icalEvent) {
203
+ let icalEvent;
204
+ if (
205
+ typeof this.mail.icalEvent === 'object' &&
206
+ (this.mail.icalEvent.content || this.mail.icalEvent.path || this.mail.icalEvent.href || this.mail.icalEvent.raw)
207
+ ) {
208
+ // an own "__proto__" key would make the copy inherit path/href from caller
209
+ // data, and the mapping below then replaces the content the caller did set
210
+ icalEvent = copyOwnKeys({}, this.mail.icalEvent);
211
+ } else {
212
+ icalEvent = {
213
+ content: this.mail.icalEvent
214
+ };
215
+ }
216
+
217
+ if (/^data:/i.test(icalEvent.path || icalEvent.href)) {
218
+ icalEvent = this._processDataUrl(icalEvent);
219
+ }
220
+
221
+ if (/^https?:\/\//i.test(icalEvent.path)) {
222
+ icalEvent.href = icalEvent.path;
223
+ icalEvent.path = undefined;
224
+ }
225
+
226
+ if (!icalEvent.raw) {
227
+ // map file path and URL values into `content`, otherwise the content
228
+ // nodes would render an empty body
229
+ if (icalEvent.path) {
230
+ icalEvent.content = {
231
+ path: icalEvent.path
232
+ };
233
+ icalEvent.path = undefined;
234
+ } else if (icalEvent.href) {
235
+ icalEvent.content = {
236
+ href: icalEvent.href,
237
+ httpHeaders: icalEvent.httpHeaders
238
+ };
239
+ icalEvent.href = undefined;
240
+ }
241
+ }
242
+
243
+ if (icalEvent.content && typeof icalEvent.content === 'object') {
244
+ // we are going to have the same attachment twice, so mark this to be
245
+ // resolved just once
246
+ icalEvent.content._resolve = true;
247
+ }
248
+
249
+ this._icalEvent = icalEvent;
250
+ }
251
+
252
+ return this._icalEvent;
186
253
  }
187
254
 
188
255
  /**
@@ -191,16 +258,14 @@ class MailComposer {
191
258
  * @returns {Array} An array of alternative elements. Includes the `text` and `html` values as well
192
259
  */
193
260
  getAlternatives() {
194
- let alternatives = [],
195
- text,
196
- html,
197
- watchHtml,
198
- amp,
199
- icalEvent,
200
- eventObject;
261
+ const alternatives = [];
262
+ let text, html, watchHtml, amp, eventObject;
201
263
 
202
264
  if (this.mail.text) {
203
- if (typeof this.mail.text === 'object' && (this.mail.text.content || this.mail.text.path || this.mail.text.href || this.mail.text.raw)) {
265
+ if (
266
+ typeof this.mail.text === 'object' &&
267
+ (this.mail.text.content || this.mail.text.path || this.mail.text.href || this.mail.text.raw)
268
+ ) {
204
269
  text = this.mail.text;
205
270
  } else {
206
271
  text = {
@@ -225,7 +290,10 @@ class MailComposer {
225
290
  }
226
291
 
227
292
  if (this.mail.amp) {
228
- if (typeof this.mail.amp === 'object' && (this.mail.amp.content || this.mail.amp.path || this.mail.amp.href || this.mail.amp.raw)) {
293
+ if (
294
+ typeof this.mail.amp === 'object' &&
295
+ (this.mail.amp.content || this.mail.amp.path || this.mail.amp.href || this.mail.amp.raw)
296
+ ) {
229
297
  amp = this.mail.amp;
230
298
  } else {
231
299
  amp = {
@@ -237,37 +305,21 @@ class MailComposer {
237
305
 
238
306
  // NB! when including attachments with a calendar alternative you might end up in a blank screen on some clients
239
307
  if (this.mail.icalEvent) {
240
- if (
241
- typeof this.mail.icalEvent === 'object' &&
242
- (this.mail.icalEvent.content || this.mail.icalEvent.path || this.mail.icalEvent.href || this.mail.icalEvent.raw)
243
- ) {
244
- icalEvent = this.mail.icalEvent;
245
- } else {
246
- icalEvent = {
247
- content: this.mail.icalEvent
248
- };
249
- }
250
-
251
- eventObject = {};
252
- Object.keys(icalEvent).forEach(key => {
253
- eventObject[key] = icalEvent[key];
254
- });
255
-
256
- if (eventObject.content && typeof eventObject.content === 'object') {
257
- // we are going to have the same attachment twice, so mark this to be
258
- // resolved just once
259
- eventObject.content._resolve = true;
260
- }
308
+ eventObject = Object.assign({}, this._getIcalEvent());
261
309
 
262
310
  eventObject.filename = false;
263
- eventObject.contentType = 'text/calendar; charset=utf-8; method=' + (eventObject.method || 'PUBLISH').toString().trim().toUpperCase();
311
+ eventObject.contentType =
312
+ 'text/calendar; charset=utf-8; method=' + (eventObject.method || 'PUBLISH').toString().trim().toUpperCase();
264
313
  if (!eventObject.headers) {
265
314
  eventObject.headers = {};
266
315
  }
267
316
  }
268
317
 
269
318
  if (this.mail.html) {
270
- if (typeof this.mail.html === 'object' && (this.mail.html.content || this.mail.html.path || this.mail.html.href || this.mail.html.raw)) {
319
+ if (
320
+ typeof this.mail.html === 'object' &&
321
+ (this.mail.html.content || this.mail.html.path || this.mail.html.href || this.mail.html.raw)
322
+ ) {
271
323
  html = this.mail.html;
272
324
  } else {
273
325
  html = {
@@ -285,14 +337,14 @@ class MailComposer {
285
337
  .concat(eventObject || [])
286
338
  .concat(this.mail.alternatives || [])
287
339
  .forEach(alternative => {
288
- let data;
289
-
290
340
  if (/^data:/i.test(alternative.path || alternative.href)) {
291
341
  alternative = this._processDataUrl(alternative);
292
342
  }
293
343
 
294
- data = {
295
- contentType: alternative.contentType || mimeFuncs.detectMimeType(alternative.filename || alternative.path || alternative.href || 'txt'),
344
+ const data = {
345
+ contentType:
346
+ alternative.contentType ||
347
+ mimeFuncs.detectMimeType(alternative.filename || alternative.path || alternative.href || 'txt'),
296
348
  contentTransferEncoding: alternative.contentTransferEncoding
297
349
  };
298
350
 
@@ -341,26 +393,22 @@ class MailComposer {
341
393
  * @returns {Object} MimeNode node element
342
394
  */
343
395
  _createMixed(parentNode) {
344
- let node;
345
-
346
- if (!parentNode) {
347
- node = new MimeNode('multipart/mixed', {
348
- baseBoundary: this.mail.baseBoundary,
349
- textEncoding: this.mail.textEncoding,
350
- boundaryPrefix: this.mail.boundaryPrefix,
351
- disableUrlAccess: this.mail.disableUrlAccess,
352
- disableFileAccess: this.mail.disableFileAccess,
353
- normalizeHeaderKey: this.mail.normalizeHeaderKey,
354
- newline: this.mail.newline
355
- });
356
- } else {
357
- node = parentNode.createChild('multipart/mixed', {
358
- disableUrlAccess: this.mail.disableUrlAccess,
359
- disableFileAccess: this.mail.disableFileAccess,
360
- normalizeHeaderKey: this.mail.normalizeHeaderKey,
361
- newline: this.mail.newline
362
- });
363
- }
396
+ const node = parentNode
397
+ ? parentNode.createChild('multipart/mixed', {
398
+ disableUrlAccess: this.mail.disableUrlAccess,
399
+ disableFileAccess: this.mail.disableFileAccess,
400
+ normalizeHeaderKey: this.mail.normalizeHeaderKey,
401
+ newline: this.mail.newline
402
+ })
403
+ : new MimeNode('multipart/mixed', {
404
+ baseBoundary: this.mail.baseBoundary,
405
+ textEncoding: this.mail.textEncoding,
406
+ boundaryPrefix: this.mail.boundaryPrefix,
407
+ disableUrlAccess: this.mail.disableUrlAccess,
408
+ disableFileAccess: this.mail.disableFileAccess,
409
+ normalizeHeaderKey: this.mail.normalizeHeaderKey,
410
+ newline: this.mail.newline
411
+ });
364
412
 
365
413
  if (this._useAlternative) {
366
414
  this._createAlternative(node);
@@ -389,26 +437,22 @@ class MailComposer {
389
437
  * @returns {Object} MimeNode node element
390
438
  */
391
439
  _createAlternative(parentNode) {
392
- let node;
393
-
394
- if (!parentNode) {
395
- node = new MimeNode('multipart/alternative', {
396
- baseBoundary: this.mail.baseBoundary,
397
- textEncoding: this.mail.textEncoding,
398
- boundaryPrefix: this.mail.boundaryPrefix,
399
- disableUrlAccess: this.mail.disableUrlAccess,
400
- disableFileAccess: this.mail.disableFileAccess,
401
- normalizeHeaderKey: this.mail.normalizeHeaderKey,
402
- newline: this.mail.newline
403
- });
404
- } else {
405
- node = parentNode.createChild('multipart/alternative', {
406
- disableUrlAccess: this.mail.disableUrlAccess,
407
- disableFileAccess: this.mail.disableFileAccess,
408
- normalizeHeaderKey: this.mail.normalizeHeaderKey,
409
- newline: this.mail.newline
410
- });
411
- }
440
+ const node = parentNode
441
+ ? parentNode.createChild('multipart/alternative', {
442
+ disableUrlAccess: this.mail.disableUrlAccess,
443
+ disableFileAccess: this.mail.disableFileAccess,
444
+ normalizeHeaderKey: this.mail.normalizeHeaderKey,
445
+ newline: this.mail.newline
446
+ })
447
+ : new MimeNode('multipart/alternative', {
448
+ baseBoundary: this.mail.baseBoundary,
449
+ textEncoding: this.mail.textEncoding,
450
+ boundaryPrefix: this.mail.boundaryPrefix,
451
+ disableUrlAccess: this.mail.disableUrlAccess,
452
+ disableFileAccess: this.mail.disableFileAccess,
453
+ normalizeHeaderKey: this.mail.normalizeHeaderKey,
454
+ newline: this.mail.newline
455
+ });
412
456
 
413
457
  this._alternatives.forEach(alternative => {
414
458
  if (this._useRelated && this._htmlNode === alternative) {
@@ -428,26 +472,22 @@ class MailComposer {
428
472
  * @returns {Object} MimeNode node element
429
473
  */
430
474
  _createRelated(parentNode) {
431
- let node;
432
-
433
- if (!parentNode) {
434
- node = new MimeNode('multipart/related; type="text/html"', {
435
- baseBoundary: this.mail.baseBoundary,
436
- textEncoding: this.mail.textEncoding,
437
- boundaryPrefix: this.mail.boundaryPrefix,
438
- disableUrlAccess: this.mail.disableUrlAccess,
439
- disableFileAccess: this.mail.disableFileAccess,
440
- normalizeHeaderKey: this.mail.normalizeHeaderKey,
441
- newline: this.mail.newline
442
- });
443
- } else {
444
- node = parentNode.createChild('multipart/related; type="text/html"', {
445
- disableUrlAccess: this.mail.disableUrlAccess,
446
- disableFileAccess: this.mail.disableFileAccess,
447
- normalizeHeaderKey: this.mail.normalizeHeaderKey,
448
- newline: this.mail.newline
449
- });
450
- }
475
+ const node = parentNode
476
+ ? parentNode.createChild('multipart/related; type="text/html"', {
477
+ disableUrlAccess: this.mail.disableUrlAccess,
478
+ disableFileAccess: this.mail.disableFileAccess,
479
+ normalizeHeaderKey: this.mail.normalizeHeaderKey,
480
+ newline: this.mail.newline
481
+ })
482
+ : new MimeNode('multipart/related; type="text/html"', {
483
+ baseBoundary: this.mail.baseBoundary,
484
+ textEncoding: this.mail.textEncoding,
485
+ boundaryPrefix: this.mail.boundaryPrefix,
486
+ disableUrlAccess: this.mail.disableUrlAccess,
487
+ disableFileAccess: this.mail.disableFileAccess,
488
+ normalizeHeaderKey: this.mail.normalizeHeaderKey,
489
+ newline: this.mail.newline
490
+ });
451
491
 
452
492
  this._createContentNode(node, this._htmlNode);
453
493
 
@@ -467,33 +507,30 @@ class MailComposer {
467
507
  element = element || {};
468
508
  element.content = element.content || '';
469
509
 
470
- let node;
471
- let encoding = (element.encoding || 'utf8')
510
+ const encoding = (element.encoding || 'utf8')
472
511
  .toString()
473
512
  .toLowerCase()
474
513
  .replace(/[-_\s]/g, '');
475
514
 
476
- if (!parentNode) {
477
- node = new MimeNode(element.contentType, {
478
- filename: element.filename,
479
- baseBoundary: this.mail.baseBoundary,
480
- textEncoding: this.mail.textEncoding,
481
- boundaryPrefix: this.mail.boundaryPrefix,
482
- disableUrlAccess: this.mail.disableUrlAccess,
483
- disableFileAccess: this.mail.disableFileAccess,
484
- normalizeHeaderKey: this.mail.normalizeHeaderKey,
485
- newline: this.mail.newline
486
- });
487
- } else {
488
- node = parentNode.createChild(element.contentType, {
489
- filename: element.filename,
490
- textEncoding: this.mail.textEncoding,
491
- disableUrlAccess: this.mail.disableUrlAccess,
492
- disableFileAccess: this.mail.disableFileAccess,
493
- normalizeHeaderKey: this.mail.normalizeHeaderKey,
494
- newline: this.mail.newline
495
- });
496
- }
515
+ const node = parentNode
516
+ ? parentNode.createChild(element.contentType, {
517
+ filename: element.filename,
518
+ textEncoding: this.mail.textEncoding,
519
+ disableUrlAccess: this.mail.disableUrlAccess,
520
+ disableFileAccess: this.mail.disableFileAccess,
521
+ normalizeHeaderKey: this.mail.normalizeHeaderKey,
522
+ newline: this.mail.newline
523
+ })
524
+ : new MimeNode(element.contentType, {
525
+ filename: element.filename,
526
+ baseBoundary: this.mail.baseBoundary,
527
+ textEncoding: this.mail.textEncoding,
528
+ boundaryPrefix: this.mail.boundaryPrefix,
529
+ disableUrlAccess: this.mail.disableUrlAccess,
530
+ disableFileAccess: this.mail.disableFileAccess,
531
+ normalizeHeaderKey: this.mail.normalizeHeaderKey,
532
+ newline: this.mail.newline
533
+ });
497
534
 
498
535
  // add custom headers
499
536
  if (element.headers) {
@@ -538,9 +575,46 @@ class MailComposer {
538
575
  * @return {Object} Parsed element
539
576
  */
540
577
  _processDataUrl(element) {
578
+ const dataUrl = element.path || element.href;
579
+
580
+ // Early validation to prevent ReDoS
581
+ if (!dataUrl || typeof dataUrl !== 'string') {
582
+ return element;
583
+ }
584
+
585
+ if (!dataUrl.startsWith('data:')) {
586
+ return element;
587
+ }
588
+
589
+ if (dataUrl.length > 52428800) {
590
+ // 52428800 chars = 50MB limit for data URL string (~37.5MB decoded image)
591
+ // Extract content type before rejecting to preserve MIME type
592
+ let detectedType = 'application/octet-stream';
593
+ const commaPos = dataUrl.indexOf(',');
594
+
595
+ if (commaPos > 0 && commaPos < 200) {
596
+ // Parse header safely with size limit
597
+ const header = dataUrl.substring(5, commaPos); // skip 'data:'
598
+ const parts = header.split(';');
599
+ if (parts[0] && parts[0].includes('/')) {
600
+ detectedType = parts[0].trim();
601
+ }
602
+ }
603
+
604
+ // Return empty content for excessively long data URLs
605
+ return Object.assign(copyOwnKeys({}, element), {
606
+ path: false,
607
+ href: false,
608
+ content: Buffer.alloc(0),
609
+ contentType: element.contentType || detectedType
610
+ });
611
+ }
612
+
541
613
  let parsedDataUri;
542
- if ((element.path || element.href).match(/^data:/)) {
543
- parsedDataUri = parseDataURI(element.path || element.href);
614
+ try {
615
+ parsedDataUri = parseDataURI(dataUrl);
616
+ } catch (_err) {
617
+ return element;
544
618
  }
545
619
 
546
620
  if (!parsedDataUri) {