mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-09 06:55:03 +00:00
fix: honor OS-level PAC configuration in system proxy mode (#7766)
This commit is contained in:
@@ -1,14 +1,17 @@
|
||||
const parseUrl = require('url').parse;
|
||||
const https = require('node:https');
|
||||
const http = require('node:http');
|
||||
const { HttpsProxyAgent } = require('https-proxy-agent');
|
||||
const { interpolateString } = require('../ipc/network/interpolate-string');
|
||||
const { SocksProxyAgent } = require('socks-proxy-agent');
|
||||
const { HttpProxyAgent } = require('http-proxy-agent');
|
||||
const { isEmpty, get, isUndefined, isNull } = require('lodash');
|
||||
const { getOrCreateHttpsAgent, getOrCreateHttpAgent } = require('@usebruno/requests');
|
||||
const {
|
||||
getOrCreateHttpsAgent,
|
||||
getOrCreateHttpAgent,
|
||||
resolveAgentsFromPac,
|
||||
PatchedHttpsProxyAgent
|
||||
} = require('@usebruno/requests');
|
||||
const { preferencesUtil } = require('../store/preferences');
|
||||
const { getPacResolver } = require('@usebruno/requests');
|
||||
|
||||
const DEFAULT_PORTS = {
|
||||
ftp: 21,
|
||||
@@ -70,40 +73,6 @@ const shouldUseProxy = (url, proxyBypass) => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Options that should be forwarded from the constructor to the target TLS upgrade.
|
||||
*/
|
||||
const TARGET_TLS_OPTIONS = ['cert', 'key', 'pfx', 'passphrase', 'rejectUnauthorized', 'secureContext'];
|
||||
|
||||
/**
|
||||
* Patched version of HttpsProxyAgent that correctly handles TLS options for
|
||||
* both the proxy connection and the target server connection.
|
||||
*
|
||||
* The upstream HttpsProxyAgent (https://github.com/TooTallNate/proxy-agents/issues/194)
|
||||
* ignores constructor options when upgrading the tunneled socket to TLS for the
|
||||
* target server. This patch forwards the relevant TLS options to the target upgrade.
|
||||
*/
|
||||
class PatchedHttpsProxyAgent extends HttpsProxyAgent {
|
||||
constructor(proxy, opts) {
|
||||
super(proxy, opts);
|
||||
this.constructorOpts = opts;
|
||||
}
|
||||
|
||||
async connect(req, opts) {
|
||||
const targetOpts = { ...opts };
|
||||
|
||||
if (this.constructorOpts) {
|
||||
for (const key of TARGET_TLS_OPTIONS) {
|
||||
if (key in this.constructorOpts) {
|
||||
targetOpts[key] = this.constructorOpts[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.connect(req, targetOpts);
|
||||
}
|
||||
}
|
||||
|
||||
async function setupProxyAgents({
|
||||
requestConfig,
|
||||
proxyMode = 'off',
|
||||
@@ -184,40 +153,58 @@ async function setupProxyAgents({
|
||||
}
|
||||
}
|
||||
} else if (proxyMode === 'system') {
|
||||
const { http_proxy, https_proxy, no_proxy } = proxyConfig || {};
|
||||
const shouldUseSystemProxy = shouldUseProxy(requestConfig.url, no_proxy || '');
|
||||
if (shouldUseSystemProxy) {
|
||||
const { http_proxy, https_proxy, no_proxy, pac_url } = proxyConfig || {};
|
||||
|
||||
// If the OS is configured with a PAC URL, resolve it using the existing PAC infrastructure
|
||||
if (pac_url) {
|
||||
if (timeline) timeline.push({ timestamp: new Date(), type: 'info', message: `Resolving system PAC: ${pac_url}` });
|
||||
try {
|
||||
if (http_proxy?.length && !isHttpsRequest) {
|
||||
const parsedHttpProxy = new URL(http_proxy);
|
||||
const isHttpsSystemProxy = parsedHttpProxy.protocol === 'https:';
|
||||
const systemHttpProxyAgentOptions = isHttpsSystemProxy ? { keepAlive: true, ...tlsOptions } : { keepAlive: true };
|
||||
if (timeline) {
|
||||
timeline.push({
|
||||
timestamp: new Date(),
|
||||
type: 'info',
|
||||
message: `Using system proxy: ${http_proxy}`
|
||||
});
|
||||
}
|
||||
requestConfig.httpAgent = getOrCreateHttpAgent({ AgentClass: HttpProxyAgent, options: systemHttpProxyAgentOptions, proxyUri: http_proxy, timeline, disableCache, hostname });
|
||||
const { directives, httpAgent, httpsAgent } = await resolveAgentsFromPac({ pacSource: pac_url, requestUrl: requestConfig.url, requestProtocol: isHttpsRequest ? 'https' : 'http', tlsOptions, httpsAgentRequestFields, timeline, disableCache, hostname });
|
||||
if (httpAgent) requestConfig.httpAgent = httpAgent;
|
||||
if (httpsAgent) requestConfig.httpsAgent = httpsAgent;
|
||||
if (directives) {
|
||||
if (timeline) { timeline.push({ timestamp: new Date(), type: 'info', message: `PAC directives: ${directives.join('; ')}` }); }
|
||||
} else {
|
||||
if (timeline) { timeline.push({ timestamp: new Date(), type: 'info', message: 'System PAC resolved: DIRECT (no proxy)' }); }
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid system http_proxy "${http_proxy}": ${error.message}`);
|
||||
} catch (err) {
|
||||
if (timeline) { timeline.push({ timestamp: new Date(), type: 'error', message: `System PAC resolution failed: ${err.message}` }); }
|
||||
}
|
||||
try {
|
||||
if (https_proxy?.length && isHttpsRequest) {
|
||||
new URL(https_proxy);
|
||||
if (timeline) {
|
||||
timeline.push({
|
||||
timestamp: new Date(),
|
||||
type: 'info',
|
||||
message: `Using system proxy: ${https_proxy}`
|
||||
});
|
||||
} else {
|
||||
const shouldUseSystemProxy = shouldUseProxy(requestConfig.url, no_proxy || '');
|
||||
if (shouldUseSystemProxy) {
|
||||
try {
|
||||
if (http_proxy?.length && !isHttpsRequest) {
|
||||
const parsedHttpProxy = new URL(http_proxy);
|
||||
const isHttpsSystemProxy = parsedHttpProxy.protocol === 'https:';
|
||||
const systemHttpProxyAgentOptions = isHttpsSystemProxy ? { keepAlive: true, ...tlsOptions } : { keepAlive: true };
|
||||
if (timeline) {
|
||||
timeline.push({
|
||||
timestamp: new Date(),
|
||||
type: 'info',
|
||||
message: `Using system proxy: ${http_proxy}`
|
||||
});
|
||||
}
|
||||
requestConfig.httpAgent = getOrCreateHttpAgent({ AgentClass: HttpProxyAgent, options: systemHttpProxyAgentOptions, proxyUri: http_proxy, timeline, disableCache, hostname });
|
||||
}
|
||||
requestConfig.httpsAgent = getOrCreateHttpsAgent({ AgentClass: PatchedHttpsProxyAgent, options: tlsOptions, proxyUri: https_proxy, timeline, disableCache, hostname });
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid system http_proxy "${http_proxy}": ${error.message}`);
|
||||
}
|
||||
try {
|
||||
if (https_proxy?.length && isHttpsRequest) {
|
||||
new URL(https_proxy);
|
||||
if (timeline) {
|
||||
timeline.push({
|
||||
timestamp: new Date(),
|
||||
type: 'info',
|
||||
message: `Using system proxy: ${https_proxy}`
|
||||
});
|
||||
}
|
||||
requestConfig.httpsAgent = getOrCreateHttpsAgent({ AgentClass: PatchedHttpsProxyAgent, options: tlsOptions, proxyUri: https_proxy, timeline, disableCache, hostname });
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid system https_proxy "${https_proxy}": ${error.message}`);
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid system https_proxy "${https_proxy}": ${error.message}`);
|
||||
}
|
||||
}
|
||||
} else if (proxyMode === 'pac') {
|
||||
@@ -225,26 +212,11 @@ async function setupProxyAgents({
|
||||
if (pacSource) {
|
||||
if (timeline) timeline.push({ timestamp: new Date(), type: 'info', message: `Resolving PAC: ${pacSource}` });
|
||||
try {
|
||||
const resolver = await getPacResolver({ pacSource, httpsAgentRequestFields });
|
||||
const directives = await resolver.resolve(requestConfig.url);
|
||||
if (directives && directives.length) {
|
||||
const first = directives[0];
|
||||
const { directives, httpAgent, httpsAgent } = await resolveAgentsFromPac({ pacSource, requestUrl: requestConfig.url, requestProtocol: isHttpsRequest ? 'https' : 'http', tlsOptions, httpsAgentRequestFields, timeline, disableCache, hostname });
|
||||
if (httpAgent) requestConfig.httpAgent = httpAgent;
|
||||
if (httpsAgent) requestConfig.httpsAgent = httpsAgent;
|
||||
if (directives) {
|
||||
if (timeline) timeline.push({ timestamp: new Date(), type: 'info', message: `PAC directives: ${directives.join('; ')}` });
|
||||
if (/^(PROXY|HTTPS?)\s+/i.test(first)) {
|
||||
const parts = first.split(/\s+/);
|
||||
const keyword = parts[0].toUpperCase();
|
||||
const hostPort = parts[1];
|
||||
const scheme = keyword === 'HTTPS' ? 'https' : 'http';
|
||||
const proxyUri = `${scheme}://${hostPort}`;
|
||||
requestConfig.httpAgent = getOrCreateHttpAgent({ AgentClass: HttpProxyAgent, options: { keepAlive: true }, proxyUri, timeline, disableCache, hostname });
|
||||
requestConfig.httpsAgent = getOrCreateHttpsAgent({ AgentClass: PatchedHttpsProxyAgent, options: tlsOptions, proxyUri, timeline, disableCache, hostname });
|
||||
} else if (/^SOCKS/i.test(first)) {
|
||||
const hostPort = first.split(/\s+/)[1];
|
||||
const proto = /^SOCKS4\s/i.test(first) ? 'socks4' : 'socks5';
|
||||
const proxyUri = `${proto}://${hostPort}`;
|
||||
requestConfig.httpAgent = getOrCreateHttpAgent({ AgentClass: SocksProxyAgent, options: { keepAlive: true }, proxyUri, timeline, disableCache, hostname });
|
||||
requestConfig.httpsAgent = getOrCreateHttpsAgent({ AgentClass: SocksProxyAgent, options: tlsOptions, proxyUri, timeline, disableCache, hostname });
|
||||
}
|
||||
} else {
|
||||
if (timeline) timeline.push({ timestamp: new Date(), type: 'info', message: 'PAC resolved: DIRECT (no proxy)' });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user