@nocobase/plugin-workflow-mailer 2.2.16 → 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
@@ -2,15 +2,74 @@
2
2
 
3
3
  const http = require('http');
4
4
  const https = require('https');
5
- const urllib = require('url');
5
+ const urllib = require('../shared/url');
6
6
  const zlib = require('zlib');
7
- const PassThrough = require('stream').PassThrough;
7
+ const { PassThrough } = require('stream');
8
8
  const Cookies = require('./cookies');
9
9
  const packageData = require('../../package.json');
10
10
  const net = require('net');
11
+ const errors = require('../errors');
12
+ const { isProtoKey } = require('../shared/objects');
11
13
 
12
14
  const MAX_REDIRECTS = 5;
13
15
 
16
+ // Only genuine TLS settings are taken from options.tls. That object reaches us straight
17
+ // from a user supplied attachment (content.tls), so keys like host, port, path, socketPath
18
+ // or lookup would otherwise repoint the request at a destination that never went through
19
+ // the URL checks below.
20
+ //
21
+ // The source of truth is the tls.connect() option list in the Node docs. A key missing
22
+ // here is dropped silently, so extend this list rather than working around it.
23
+ const TLS_OPTION_KEYS = [
24
+ 'ALPNProtocols',
25
+ 'ca',
26
+ 'cert',
27
+ 'checkServerIdentity',
28
+ 'ciphers',
29
+ 'crl',
30
+ 'dhparam',
31
+ 'ecdhCurve',
32
+ 'honorCipherOrder',
33
+ 'key',
34
+ 'maxVersion',
35
+ 'minVersion',
36
+ 'passphrase',
37
+ 'pfx',
38
+ 'rejectUnauthorized',
39
+ 'secureContext',
40
+ 'secureOptions',
41
+ 'secureProtocol',
42
+ 'servername',
43
+ 'sessionIdContext',
44
+ 'sigalgs'
45
+ ];
46
+
47
+ /**
48
+ * Resolves a URL only if it is one this module is willing to request.
49
+ *
50
+ * urllib.parse throws for a host that contains forbidden bytes, and it is called for
51
+ * every URL that reaches nmfetch, including ones that arrive from a message attachment
52
+ * or from a redirect Location header. An uncaught throw here takes the process down,
53
+ * so a URL that does not parse is reported the same way as one with a scheme we refuse.
54
+ *
55
+ * @param {String} url URL to parse
56
+ * @returns {Object|Boolean} Parsed URL, or false if it is not a usable http(s) URL
57
+ */
58
+ function parseFetchUrl(url) {
59
+ let parsed;
60
+ try {
61
+ parsed = urllib.parse(url);
62
+ } catch (_err) {
63
+ return false;
64
+ }
65
+
66
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
67
+ return false;
68
+ }
69
+
70
+ return parsed;
71
+ }
72
+
14
73
  module.exports = function (url, options) {
15
74
  return nmfetch(url, options);
16
75
  };
@@ -25,6 +84,30 @@ function nmfetch(url, options) {
25
84
  options.redirects = options.redirects || 0;
26
85
  options.maxRedirects = isNaN(options.maxRedirects) ? MAX_REDIRECTS : options.maxRedirects;
27
86
 
87
+ const fetchRes = options.fetchRes;
88
+ const parsed = parseFetchUrl(url);
89
+
90
+ if (!parsed) {
91
+ // Only http(s) URLs can be fetched. Any other scheme (file:, gopher:, a
92
+ // protocol-relative redirect target etc.) would otherwise be silently served over
93
+ // plain HTTP, possibly against an unintended host. Bail out before the cookie jar
94
+ // is touched so a refused URL can not seed it, and release a caller supplied body:
95
+ // this is the one exit that runs before the error handler below is attached to it,
96
+ // so an error on that stream would have nowhere to go and the fd or socket behind
97
+ // it would never be released.
98
+ if (options.body && typeof options.body.destroy === 'function') {
99
+ options.body.on('error', () => false);
100
+ options.body.destroy();
101
+ }
102
+ setImmediate(() => {
103
+ const err = new Error('Unsupported protocol for URL ' + url);
104
+ err.code = errors.EFETCH;
105
+ err.sourceUrl = url;
106
+ fetchRes.emit('error', err);
107
+ });
108
+ return fetchRes;
109
+ }
110
+
28
111
  if (options.cookie) {
29
112
  [].concat(options.cookie || []).forEach(cookie => {
30
113
  options.cookies.set(cookie, url);
@@ -32,21 +115,23 @@ function nmfetch(url, options) {
32
115
  options.cookie = false;
33
116
  }
34
117
 
35
- let fetchRes = options.fetchRes;
36
- let parsed = urllib.parse(url);
37
118
  let method = (options.method || '').toString().trim().toUpperCase() || 'GET';
38
119
  let finished = false;
39
120
  let cookies;
40
121
  let body;
41
122
 
42
- let handler = parsed.protocol === 'https:' ? https : http;
123
+ const handler = parsed.protocol === 'https:' ? https : http;
43
124
 
44
- let headers = {
125
+ const headers = {
45
126
  'accept-encoding': 'gzip,deflate',
46
127
  'user-agent': 'nodemailer/' + packageData.version
47
128
  };
48
129
 
49
130
  Object.keys(options.headers || {}).forEach(key => {
131
+ // options.headers is the caller's httpHeaders, straight off an attachment
132
+ if (isProtoKey(key.toLowerCase().trim())) {
133
+ return;
134
+ }
50
135
  headers[key.toLowerCase().trim()] = options.headers[key];
51
136
  });
52
137
 
@@ -76,7 +161,7 @@ function nmfetch(url, options) {
76
161
  return;
77
162
  }
78
163
  finished = true;
79
- err.type = 'FETCH';
164
+ err.code = errors.EFETCH;
80
165
  err.sourceUrl = url;
81
166
  fetchRes.emit('error', err);
82
167
  });
@@ -89,7 +174,7 @@ function nmfetch(url, options) {
89
174
  body = Buffer.from(
90
175
  Object.keys(options.body)
91
176
  .map(key => {
92
- let value = options.body[key].toString().trim();
177
+ const value = options.body[key].toString().trim();
93
178
  return encodeURIComponent(key) + '=' + encodeURIComponent(value);
94
179
  })
95
180
  .join('&')
@@ -99,7 +184,7 @@ function nmfetch(url, options) {
99
184
  return;
100
185
  }
101
186
  finished = true;
102
- E.type = 'FETCH';
187
+ E.code = errors.EFETCH;
103
188
  E.sourceUrl = url;
104
189
  fetchRes.emit('error', E);
105
190
  return;
@@ -116,23 +201,35 @@ function nmfetch(url, options) {
116
201
  }
117
202
 
118
203
  let req;
119
- let reqOptions = {
204
+ const reqOptions = {
120
205
  method,
121
206
  host: parsed.hostname,
122
207
  path: parsed.path,
123
208
  port: parsed.port ? parsed.port : parsed.protocol === 'https:' ? 443 : 80,
124
209
  headers,
125
- rejectUnauthorized: false,
210
+ // Validate TLS certificates by default. Callers that genuinely need to
211
+ // reach a self-signed/internal host opt out explicitly with
212
+ // options.tls = { rejectUnauthorized: false }.
213
+ rejectUnauthorized: true,
126
214
  agent: false
127
215
  };
128
216
 
129
217
  if (options.tls) {
218
+ // see TLS_OPTION_KEYS
130
219
  Object.keys(options.tls).forEach(key => {
131
- reqOptions[key] = options.tls[key];
220
+ if (TLS_OPTION_KEYS.includes(key)) {
221
+ reqOptions[key] = options.tls[key];
222
+ }
132
223
  });
133
224
  }
134
225
 
135
- if (parsed.protocol === 'https:' && parsed.hostname && parsed.hostname !== reqOptions.host && !net.isIP(parsed.hostname) && !reqOptions.servername) {
226
+ if (
227
+ parsed.protocol === 'https:' &&
228
+ parsed.hostname &&
229
+ parsed.hostname !== reqOptions.host &&
230
+ !net.isIP(parsed.hostname) &&
231
+ !reqOptions.servername
232
+ ) {
136
233
  reqOptions.servername = parsed.hostname;
137
234
  }
138
235
 
@@ -141,7 +238,7 @@ function nmfetch(url, options) {
141
238
  } catch (E) {
142
239
  finished = true;
143
240
  setImmediate(() => {
144
- E.type = 'FETCH';
241
+ E.code = errors.EFETCH;
145
242
  E.sourceUrl = url;
146
243
  fetchRes.emit('error', E);
147
244
  });
@@ -155,8 +252,8 @@ function nmfetch(url, options) {
155
252
  }
156
253
  finished = true;
157
254
  req.abort();
158
- let err = new Error('Request Timeout');
159
- err.type = 'FETCH';
255
+ const err = new Error('Request Timeout');
256
+ err.code = errors.EFETCH;
160
257
  err.sourceUrl = url;
161
258
  fetchRes.emit('error', err);
162
259
  });
@@ -167,7 +264,7 @@ function nmfetch(url, options) {
167
264
  return;
168
265
  }
169
266
  finished = true;
170
- err.type = 'FETCH';
267
+ err.code = errors.EFETCH;
171
268
  err.sourceUrl = url;
172
269
  fetchRes.emit('error', err);
173
270
  });
@@ -197,8 +294,8 @@ function nmfetch(url, options) {
197
294
  options.redirects++;
198
295
  if (options.redirects > options.maxRedirects) {
199
296
  finished = true;
200
- let err = new Error('Maximum redirect count exceeded');
201
- err.type = 'FETCH';
297
+ const err = new Error('Maximum redirect count exceeded');
298
+ err.code = errors.EFETCH;
202
299
  err.sourceUrl = url;
203
300
  fetchRes.emit('error', err);
204
301
  req.abort();
@@ -207,7 +304,48 @@ function nmfetch(url, options) {
207
304
  // redirect does not include POST body
208
305
  options.method = 'GET';
209
306
  options.body = false;
210
- return nmfetch(urllib.resolve(url, res.headers.location), options);
307
+
308
+ let redirectUrl;
309
+ try {
310
+ redirectUrl = urllib.resolve(url, res.headers.location);
311
+ } catch (_err) {
312
+ // the legacy resolver throws on a Location the WHATWG parser also refused,
313
+ // so fall through to the check below with what the server actually sent
314
+ redirectUrl = res.headers.location;
315
+ }
316
+ const redirectParsed = parseFetchUrl(redirectUrl);
317
+
318
+ if (!redirectParsed) {
319
+ // Refuse the redirect target here rather than leaving it to the recursive
320
+ // call: that call gets its own `finished` flag and no handle on this
321
+ // request, so this one would stay open and could emit a second error on
322
+ // the shared fetchRes once it times out. Callers listen with req.once().
323
+ finished = true;
324
+ const err = new Error('Unsupported protocol for URL ' + redirectUrl);
325
+ err.code = errors.EFETCH;
326
+ err.sourceUrl = redirectUrl;
327
+ fetchRes.emit('error', err);
328
+ req.abort();
329
+ return;
330
+ }
331
+
332
+ // Do not forward credentials when the redirect leaves the original
333
+ // security context: a different host, or a downgrade from https to
334
+ // http (which would otherwise put them on the wire in cleartext).
335
+ // Strip sensitive request headers so an attacker who controls the
336
+ // redirect target cannot harvest them.
337
+ const crossHost = redirectParsed.hostname !== parsed.hostname;
338
+ const downgrade = parsed.protocol === 'https:' && redirectParsed.protocol === 'http:';
339
+ if (options.headers && (crossHost || downgrade)) {
340
+ const sensitive = ['authorization', 'cookie', 'proxy-authorization'];
341
+ Object.keys(options.headers).forEach(key => {
342
+ if (sensitive.includes(key.toLowerCase())) {
343
+ delete options.headers[key];
344
+ }
345
+ });
346
+ }
347
+
348
+ return nmfetch(redirectUrl, options);
211
349
  }
212
350
 
213
351
  fetchRes.statusCode = res.statusCode;
@@ -215,8 +353,8 @@ function nmfetch(url, options) {
215
353
 
216
354
  if (res.statusCode >= 300 && !options.allowErrorResponse) {
217
355
  finished = true;
218
- let err = new Error('Invalid status code ' + res.statusCode);
219
- err.type = 'FETCH';
356
+ const err = new Error('Invalid status code ' + res.statusCode);
357
+ err.code = errors.EFETCH;
220
358
  err.sourceUrl = url;
221
359
  fetchRes.emit('error', err);
222
360
  req.abort();
@@ -228,7 +366,7 @@ function nmfetch(url, options) {
228
366
  return;
229
367
  }
230
368
  finished = true;
231
- err.type = 'FETCH';
369
+ err.code = errors.EFETCH;
232
370
  err.sourceUrl = url;
233
371
  fetchRes.emit('error', err);
234
372
  req.abort();
@@ -241,7 +379,7 @@ function nmfetch(url, options) {
241
379
  return;
242
380
  }
243
381
  finished = true;
244
- err.type = 'FETCH';
382
+ err.code = errors.EFETCH;
245
383
  err.sourceUrl = url;
246
384
  fetchRes.emit('error', err);
247
385
  req.abort();
@@ -256,12 +394,11 @@ function nmfetch(url, options) {
256
394
  try {
257
395
  if (typeof body.pipe === 'function') {
258
396
  return body.pipe(req);
259
- } else {
260
- req.write(body);
261
397
  }
398
+ req.write(body);
262
399
  } catch (err) {
263
400
  finished = true;
264
- err.type = 'FETCH';
401
+ err.code = errors.EFETCH;
265
402
  err.sourceUrl = url;
266
403
  fetchRes.emit('error', err);
267
404
  return;
@@ -13,7 +13,7 @@ class JSONTransport {
13
13
  constructor(options) {
14
14
  options = options || {};
15
15
 
16
- this.options = options || {};
16
+ this.options = options;
17
17
 
18
18
  this.name = 'JSONTransport';
19
19
  this.version = packageData.version;
@@ -33,10 +33,10 @@ class JSONTransport {
33
33
  // Sendmail strips this header line by itself
34
34
  mail.message.keepBcc = true;
35
35
 
36
- let envelope = mail.data.envelope || mail.message.getEnvelope();
37
- let messageId = mail.message.messageId();
36
+ const envelope = mail.message.getEnvelope();
37
+ const messageId = mail.message.messageId();
38
38
 
39
- let recipients = [].concat(envelope.to || []);
39
+ const recipients = [].concat(envelope.to || []);
40
40
  if (recipients.length > 3) {
41
41
  recipients.push('...and ' + recipients.splice(2).length + ' more');
42
42
  }