@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.
- package/dist/externalVersion.js +6 -6
- package/dist/node_modules/joi/dist/joi-browser.min.js +1 -1
- package/dist/node_modules/joi/lib/index.js +1 -1
- package/dist/node_modules/joi/lib/messages.js +35 -6
- package/dist/node_modules/joi/lib/types/keys.js +18 -1
- package/dist/node_modules/joi/lib/types/link.js +18 -2
- package/dist/node_modules/joi/lib/types/string.js +2 -2
- package/dist/node_modules/joi/package.json +1 -1
- package/dist/node_modules/nodemailer/.ncurc.js +2 -0
- package/dist/node_modules/nodemailer/.prettierignore +8 -0
- package/dist/node_modules/nodemailer/.prettierrc +12 -0
- package/dist/node_modules/nodemailer/.prettierrc.js +2 -0
- package/dist/node_modules/nodemailer/.release-please-config.json +9 -0
- package/dist/node_modules/nodemailer/eslint.config.js +88 -0
- package/dist/node_modules/nodemailer/lib/addressparser/index.js +314 -71
- package/dist/node_modules/nodemailer/lib/base64/index.js +9 -13
- package/dist/node_modules/nodemailer/lib/dkim/index.js +17 -18
- package/dist/node_modules/nodemailer/lib/dkim/message-parser.js +12 -13
- package/dist/node_modules/nodemailer/lib/dkim/relaxed-body.js +3 -3
- package/dist/node_modules/nodemailer/lib/dkim/sign.js +22 -18
- package/dist/node_modules/nodemailer/lib/errors.js +59 -0
- package/dist/node_modules/nodemailer/lib/fetch/cookies.js +14 -19
- package/dist/node_modules/nodemailer/lib/fetch/index.js +164 -27
- package/dist/node_modules/nodemailer/lib/json-transport/index.js +4 -4
- package/dist/node_modules/nodemailer/lib/mail-composer/index.js +229 -155
- package/dist/node_modules/nodemailer/lib/mailer/index.js +106 -54
- package/dist/node_modules/nodemailer/lib/mailer/mail-message.js +106 -68
- package/dist/node_modules/nodemailer/lib/mime-funcs/index.js +105 -59
- package/dist/node_modules/nodemailer/lib/mime-funcs/mime-types.js +21 -14
- package/dist/node_modules/nodemailer/lib/mime-node/index.js +389 -137
- package/dist/node_modules/nodemailer/lib/mime-node/last-newline.js +1 -1
- package/dist/node_modules/nodemailer/lib/mime-node/le-unix.js +4 -7
- package/dist/node_modules/nodemailer/lib/mime-node/le-windows.js +1 -4
- package/dist/node_modules/nodemailer/lib/nodemailer.js +1 -1
- package/dist/node_modules/nodemailer/lib/punycode/index.js +1 -1
- package/dist/node_modules/nodemailer/lib/qp/index.js +33 -22
- package/dist/node_modules/nodemailer/lib/sendmail-transport/index.js +54 -44
- package/dist/node_modules/nodemailer/lib/ses-transport/index.js +110 -193
- package/dist/node_modules/nodemailer/lib/shared/index.js +299 -240
- package/dist/node_modules/nodemailer/lib/shared/objects.js +42 -0
- package/dist/node_modules/nodemailer/lib/shared/url.js +151 -0
- package/dist/node_modules/nodemailer/lib/smtp-connection/data-stream.js +3 -6
- package/dist/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js +50 -19
- package/dist/node_modules/nodemailer/lib/smtp-connection/index.js +415 -223
- package/dist/node_modules/nodemailer/lib/smtp-pool/index.js +27 -34
- package/dist/node_modules/nodemailer/lib/smtp-pool/pool-resource.js +19 -16
- package/dist/node_modules/nodemailer/lib/smtp-transport/index.js +66 -53
- package/dist/node_modules/nodemailer/lib/stream-transport/index.js +16 -7
- package/dist/node_modules/nodemailer/lib/well-known/index.js +7 -7
- package/dist/node_modules/nodemailer/lib/well-known/services.json +328 -38
- package/dist/node_modules/nodemailer/lib/xoauth2/index.js +105 -33
- package/dist/node_modules/nodemailer/package.json +1 -1
- 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')
|
|
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
|
-
|
|
123
|
+
const handler = parsed.protocol === 'https:' ? https : http;
|
|
43
124
|
|
|
44
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
220
|
+
if (TLS_OPTION_KEYS.includes(key)) {
|
|
221
|
+
reqOptions[key] = options.tls[key];
|
|
222
|
+
}
|
|
132
223
|
});
|
|
133
224
|
}
|
|
134
225
|
|
|
135
|
-
if (
|
|
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.
|
|
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
|
-
|
|
159
|
-
err.
|
|
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.
|
|
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
|
-
|
|
201
|
-
err.
|
|
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
|
-
|
|
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
|
-
|
|
219
|
-
err.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
37
|
-
|
|
36
|
+
const envelope = mail.message.getEnvelope();
|
|
37
|
+
const messageId = mail.message.messageId();
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
const recipients = [].concat(envelope.to || []);
|
|
40
40
|
if (recipients.length > 3) {
|
|
41
41
|
recipients.push('...and ' + recipients.splice(2).length + ' more');
|
|
42
42
|
}
|