mirror of
https://github.com/dawidd6/action-send-mail.git
synced 2026-09-09 05:56:37 +07:00
node_modules: update (#314)
Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com>
This commit is contained in:
+115
-5
@@ -9,9 +9,67 @@ const Cookies = require('./cookies');
|
||||
const packageData = require('../../package.json');
|
||||
const net = require('net');
|
||||
const errors = require('../errors');
|
||||
const { isProtoKey } = require('../shared/objects');
|
||||
|
||||
const MAX_REDIRECTS = 5;
|
||||
|
||||
// Only genuine TLS settings are taken from options.tls. That object reaches us straight
|
||||
// from a user supplied attachment (content.tls), so keys like host, port, path, socketPath
|
||||
// or lookup would otherwise repoint the request at a destination that never went through
|
||||
// the URL checks below.
|
||||
//
|
||||
// The source of truth is the tls.connect() option list in the Node docs. A key missing
|
||||
// here is dropped silently, so extend this list rather than working around it.
|
||||
const TLS_OPTION_KEYS = [
|
||||
'ALPNProtocols',
|
||||
'ca',
|
||||
'cert',
|
||||
'checkServerIdentity',
|
||||
'ciphers',
|
||||
'crl',
|
||||
'dhparam',
|
||||
'ecdhCurve',
|
||||
'honorCipherOrder',
|
||||
'key',
|
||||
'maxVersion',
|
||||
'minVersion',
|
||||
'passphrase',
|
||||
'pfx',
|
||||
'rejectUnauthorized',
|
||||
'secureContext',
|
||||
'secureOptions',
|
||||
'secureProtocol',
|
||||
'servername',
|
||||
'sessionIdContext',
|
||||
'sigalgs'
|
||||
];
|
||||
|
||||
/**
|
||||
* Resolves a URL only if it is one this module is willing to request.
|
||||
*
|
||||
* urllib.parse throws for a host that contains forbidden bytes, and it is called for
|
||||
* every URL that reaches nmfetch, including ones that arrive from a message attachment
|
||||
* or from a redirect Location header. An uncaught throw here takes the process down,
|
||||
* so a URL that does not parse is reported the same way as one with a scheme we refuse.
|
||||
*
|
||||
* @param {String} url URL to parse
|
||||
* @returns {Object|Boolean} Parsed URL, or false if it is not a usable http(s) URL
|
||||
*/
|
||||
function parseFetchUrl(url) {
|
||||
let parsed;
|
||||
try {
|
||||
parsed = urllib.parse(url);
|
||||
} catch (_err) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
module.exports = function (url, options) {
|
||||
return nmfetch(url, options);
|
||||
};
|
||||
@@ -26,6 +84,30 @@ function nmfetch(url, options) {
|
||||
options.redirects = options.redirects || 0;
|
||||
options.maxRedirects = isNaN(options.maxRedirects) ? MAX_REDIRECTS : options.maxRedirects;
|
||||
|
||||
const fetchRes = options.fetchRes;
|
||||
const parsed = parseFetchUrl(url);
|
||||
|
||||
if (!parsed) {
|
||||
// Only http(s) URLs can be fetched. Any other scheme (file:, gopher:, a
|
||||
// protocol-relative redirect target etc.) would otherwise be silently served over
|
||||
// plain HTTP, possibly against an unintended host. Bail out before the cookie jar
|
||||
// is touched so a refused URL can not seed it, and release a caller supplied body:
|
||||
// this is the one exit that runs before the error handler below is attached to it,
|
||||
// so an error on that stream would have nowhere to go and the fd or socket behind
|
||||
// it would never be released.
|
||||
if (options.body && typeof options.body.destroy === 'function') {
|
||||
options.body.on('error', () => false);
|
||||
options.body.destroy();
|
||||
}
|
||||
setImmediate(() => {
|
||||
const err = new Error('Unsupported protocol for URL ' + url);
|
||||
err.code = errors.EFETCH;
|
||||
err.sourceUrl = url;
|
||||
fetchRes.emit('error', err);
|
||||
});
|
||||
return fetchRes;
|
||||
}
|
||||
|
||||
if (options.cookie) {
|
||||
[].concat(options.cookie || []).forEach(cookie => {
|
||||
options.cookies.set(cookie, url);
|
||||
@@ -33,8 +115,6 @@ function nmfetch(url, options) {
|
||||
options.cookie = false;
|
||||
}
|
||||
|
||||
const fetchRes = options.fetchRes;
|
||||
const parsed = urllib.parse(url);
|
||||
let method = (options.method || '').toString().trim().toUpperCase() || 'GET';
|
||||
let finished = false;
|
||||
let cookies;
|
||||
@@ -48,6 +128,10 @@ function nmfetch(url, options) {
|
||||
};
|
||||
|
||||
Object.keys(options.headers || {}).forEach(key => {
|
||||
// options.headers is the caller's httpHeaders, straight off an attachment
|
||||
if (isProtoKey(key.toLowerCase().trim())) {
|
||||
return;
|
||||
}
|
||||
headers[key.toLowerCase().trim()] = options.headers[key];
|
||||
});
|
||||
|
||||
@@ -131,7 +215,12 @@ function nmfetch(url, options) {
|
||||
};
|
||||
|
||||
if (options.tls) {
|
||||
Object.assign(reqOptions, options.tls);
|
||||
// see TLS_OPTION_KEYS
|
||||
Object.keys(options.tls).forEach(key => {
|
||||
if (TLS_OPTION_KEYS.includes(key)) {
|
||||
reqOptions[key] = options.tls[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -216,8 +305,29 @@ function nmfetch(url, options) {
|
||||
options.method = 'GET';
|
||||
options.body = false;
|
||||
|
||||
const redirectUrl = urllib.resolve(url, res.headers.location);
|
||||
const redirectParsed = urllib.parse(redirectUrl);
|
||||
let redirectUrl;
|
||||
try {
|
||||
redirectUrl = urllib.resolve(url, res.headers.location);
|
||||
} catch (_err) {
|
||||
// the legacy resolver throws on a Location the WHATWG parser also refused,
|
||||
// so fall through to the check below with what the server actually sent
|
||||
redirectUrl = res.headers.location;
|
||||
}
|
||||
const redirectParsed = parseFetchUrl(redirectUrl);
|
||||
|
||||
if (!redirectParsed) {
|
||||
// Refuse the redirect target here rather than leaving it to the recursive
|
||||
// call: that call gets its own `finished` flag and no handle on this
|
||||
// request, so this one would stay open and could emit a second error on
|
||||
// the shared fetchRes once it times out. Callers listen with req.once().
|
||||
finished = true;
|
||||
const err = new Error('Unsupported protocol for URL ' + redirectUrl);
|
||||
err.code = errors.EFETCH;
|
||||
err.sourceUrl = redirectUrl;
|
||||
fetchRes.emit('error', err);
|
||||
req.abort();
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not forward credentials when the redirect leaves the original
|
||||
// security context: a different host, or a downgrade from https to
|
||||
|
||||
Reference in New Issue
Block a user