mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
237 lines
8.9 KiB
JavaScript
237 lines
8.9 KiB
JavaScript
const fs = require('node:fs');
|
|
const path = require('path');
|
|
const { get } = require('lodash');
|
|
const { getCACertificates } = require('@usebruno/requests');
|
|
const { preferencesUtil } = require('../../store/preferences');
|
|
const { getBrunoConfig } = require('../../store/bruno-config');
|
|
const { getCachedSystemProxy } = require('../../store/system-proxy');
|
|
const { interpolateString, interpolateObject } = require('./interpolate-string');
|
|
|
|
/**
|
|
* Gets certificates and proxy configuration for a request
|
|
*/
|
|
const getCertsAndProxyConfig = async ({
|
|
collectionUid,
|
|
collection,
|
|
request,
|
|
envVars,
|
|
runtimeVariables,
|
|
processEnvVars,
|
|
collectionPath,
|
|
globalEnvironmentVariables
|
|
}) => {
|
|
/**
|
|
* @see https://github.com/usebruno/bruno/issues/211 set keepAlive to true, this should fix socket hang up errors
|
|
* @see https://github.com/nodejs/node/pull/43522 keepAlive was changed to true globally on Node v19+
|
|
*/
|
|
const httpsAgentRequestFields = { keepAlive: true };
|
|
if (!preferencesUtil.shouldVerifyTls()) {
|
|
httpsAgentRequestFields['rejectUnauthorized'] = false;
|
|
}
|
|
|
|
let caCertificates = '';
|
|
let caCertificatesCount = { system: 0, root: 0, custom: 0, extra: 0 };
|
|
|
|
// Only load CA certificates if SSL validation is enabled (otherwise they're unused)
|
|
if (preferencesUtil.shouldVerifyTls()) {
|
|
let caCertFilePath = preferencesUtil.shouldUseCustomCaCertificate() && preferencesUtil.getCustomCaCertificateFilePath();
|
|
let caCertificatesData = getCACertificates({
|
|
caCertFilePath,
|
|
shouldKeepDefaultCerts: preferencesUtil.shouldKeepDefaultCaCertificates()
|
|
});
|
|
|
|
caCertificates = caCertificatesData.caCertificates;
|
|
caCertificatesCount = caCertificatesData.caCertificatesCount;
|
|
}
|
|
|
|
// configure HTTPS agent with aggregated CA certificates
|
|
httpsAgentRequestFields['caCertificatesCount'] = caCertificatesCount;
|
|
httpsAgentRequestFields['ca'] = caCertificates || [];
|
|
|
|
const { promptVariables } = collection;
|
|
const collectionVariables = request.collectionVariables || {};
|
|
const folderVariables = request.folderVariables || {};
|
|
const requestVariables = request.requestVariables || {};
|
|
|
|
const brunoConfig = getBrunoConfig(collectionUid, collection);
|
|
const interpolationOptions = {
|
|
globalEnvironmentVariables,
|
|
collectionVariables,
|
|
envVars,
|
|
folderVariables,
|
|
requestVariables,
|
|
runtimeVariables,
|
|
promptVariables,
|
|
processEnvVars
|
|
};
|
|
|
|
// client certificate config
|
|
const clientCertConfig = get(brunoConfig, 'clientCertificates.certs', []);
|
|
|
|
for (let clientCert of clientCertConfig) {
|
|
const domain = interpolateString(clientCert?.domain, interpolationOptions);
|
|
const type = clientCert?.type || 'cert';
|
|
if (domain) {
|
|
const hostRegex = '^(https:\\/\\/|grpc:\\/\\/|grpcs:\\/\\/|ws:\\/\\/|wss:\\/\\/)?'
|
|
+ domain.replaceAll('.', '\\.').replaceAll('*', '.*');
|
|
const requestUrl = interpolateString(request.url, interpolationOptions);
|
|
if (requestUrl && requestUrl.match(hostRegex)) {
|
|
if (type === 'cert') {
|
|
try {
|
|
let certFilePath = interpolateString(clientCert?.certFilePath, interpolationOptions);
|
|
certFilePath = path.isAbsolute(certFilePath) ? certFilePath : path.join(collectionPath, certFilePath);
|
|
let keyFilePath = interpolateString(clientCert?.keyFilePath, interpolationOptions);
|
|
keyFilePath = path.isAbsolute(keyFilePath) ? keyFilePath : path.join(collectionPath, keyFilePath);
|
|
|
|
httpsAgentRequestFields['cert'] = fs.readFileSync(certFilePath);
|
|
httpsAgentRequestFields['key'] = fs.readFileSync(keyFilePath);
|
|
} catch (err) {
|
|
console.error('Error reading cert/key file', err);
|
|
throw new Error('Error reading cert/key file' + err);
|
|
}
|
|
} else if (type === 'pfx') {
|
|
try {
|
|
let pfxFilePath = interpolateString(clientCert?.pfxFilePath, interpolationOptions);
|
|
pfxFilePath = path.isAbsolute(pfxFilePath) ? pfxFilePath : path.join(collectionPath, pfxFilePath);
|
|
httpsAgentRequestFields['pfx'] = fs.readFileSync(pfxFilePath);
|
|
} catch (err) {
|
|
console.error('Error reading pfx file', err);
|
|
throw new Error('Error reading pfx file' + err);
|
|
}
|
|
}
|
|
httpsAgentRequestFields['passphrase'] = interpolateString(clientCert.passphrase, interpolationOptions);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Proxy configuration
|
|
*
|
|
* New format:
|
|
* - disabled: boolean (optional, defaults to false)
|
|
* - inherit: boolean (required)
|
|
* - config: { protocol, hostname, port, auth, bypassProxy }
|
|
*
|
|
* When collection proxy has inherit=false and disabled=false, use collection-specific proxy
|
|
* When collection proxy has inherit=true, inherit from global preferences
|
|
* When disabled=true, proxy is disabled
|
|
*
|
|
* Below logic calculates the proxyMode and proxyConfig to be used for the request
|
|
*/
|
|
let proxyMode = 'off';
|
|
let proxyConfig = {};
|
|
let proxyModeReason = '';
|
|
|
|
const collectionProxyConfig = get(brunoConfig, 'proxy', {});
|
|
const collectionProxyDisabled = get(collectionProxyConfig, 'disabled', false);
|
|
const collectionProxyInherit = get(collectionProxyConfig, 'inherit', true);
|
|
const collectionProxyConfigData = get(collectionProxyConfig, 'config', collectionProxyConfig);
|
|
|
|
if (!collectionProxyDisabled && !collectionProxyInherit) {
|
|
// Use collection-specific proxy
|
|
proxyConfig = collectionProxyConfigData;
|
|
proxyMode = 'on';
|
|
} else if (!collectionProxyDisabled && collectionProxyInherit) {
|
|
// Inherit from global preferences
|
|
const globalProxy = preferencesUtil.getGlobalProxyConfig();
|
|
const globalDisabled = get(globalProxy, 'disabled', false);
|
|
const globalProxySource = get(globalProxy, 'source', 'manual');
|
|
const globalProxyConfigData = get(globalProxy, 'config', {});
|
|
|
|
if (!globalDisabled) {
|
|
if (globalProxySource === 'pac') {
|
|
proxyMode = 'pac';
|
|
proxyConfig = {
|
|
pac: globalProxy.pac ?? {}
|
|
};
|
|
} else if (globalProxySource === 'inherit') {
|
|
proxyMode = 'system';
|
|
const systemProxyConfig = await getCachedSystemProxy();
|
|
proxyConfig = systemProxyConfig || { http_proxy: null, https_proxy: null, no_proxy: null, pac_url: null, source: 'cache-miss' };
|
|
} else {
|
|
// source === 'manual'
|
|
proxyConfig = globalProxyConfigData;
|
|
proxyMode = 'on';
|
|
}
|
|
} else {
|
|
proxyModeReason = 'App-level proxy is disabled';
|
|
}
|
|
} else {
|
|
proxyModeReason = 'Collection-level proxy is disabled';
|
|
}
|
|
|
|
return { proxyMode, proxyModeReason, proxyConfig, httpsAgentRequestFields, interpolationOptions };
|
|
};
|
|
|
|
/**
|
|
* Builds the certsAndProxyConfig object for bru.sendRequest
|
|
* This allows bru.sendRequest to use the same proxy/certs config as the main request
|
|
*/
|
|
const buildCertsAndProxyConfig = async ({
|
|
collectionUid,
|
|
collection,
|
|
collectionPath,
|
|
envVars,
|
|
runtimeVariables,
|
|
processEnvVars,
|
|
request
|
|
}) => {
|
|
const brunoConfig = getBrunoConfig(collectionUid, collection);
|
|
|
|
// Build interpolation options (same pattern as getCertsAndProxyConfig)
|
|
const globalEnvironmentVariables = collection.globalEnvironmentVariables || {};
|
|
const { promptVariables } = collection;
|
|
const collectionVariables = request?.collectionVariables || {};
|
|
const folderVariables = request?.folderVariables || {};
|
|
const requestVariables = request?.requestVariables || {};
|
|
|
|
const interpolationOptions = {
|
|
globalEnvironmentVariables,
|
|
collectionVariables,
|
|
envVars,
|
|
folderVariables,
|
|
requestVariables,
|
|
runtimeVariables,
|
|
promptVariables,
|
|
processEnvVars
|
|
};
|
|
|
|
// Build options for getHttpHttpsAgents
|
|
const options = {
|
|
noproxy: false,
|
|
shouldVerifyTls: preferencesUtil.shouldVerifyTls(),
|
|
shouldUseCustomCaCertificate: preferencesUtil.shouldUseCustomCaCertificate(),
|
|
customCaCertificateFilePath: preferencesUtil.getCustomCaCertificateFilePath(),
|
|
shouldKeepDefaultCaCertificates: preferencesUtil.shouldKeepDefaultCaCertificates(),
|
|
cacheSslSession: preferencesUtil.isSslSessionCachingEnabled()
|
|
};
|
|
|
|
// Get client certificates from bruno config and interpolate
|
|
const rawClientCertificates = get(brunoConfig, 'clientCertificates');
|
|
const clientCertificates = rawClientCertificates
|
|
? interpolateObject(rawClientCertificates, interpolationOptions)
|
|
: undefined;
|
|
|
|
// Get proxy config from bruno config and interpolate
|
|
const collectionProxyConfig = get(brunoConfig, 'proxy', {});
|
|
const collectionLevelProxy = interpolateObject(collectionProxyConfig, interpolationOptions);
|
|
|
|
// Get app-level proxy config from global preferences
|
|
const appLevelProxyConfig = preferencesUtil.getGlobalProxyConfig();
|
|
|
|
// Get system proxy config
|
|
const systemProxyConfig = await getCachedSystemProxy();
|
|
|
|
return {
|
|
collectionPath,
|
|
options,
|
|
clientCertificates,
|
|
collectionLevelProxy,
|
|
appLevelProxyConfig,
|
|
systemProxyConfig
|
|
};
|
|
};
|
|
|
|
module.exports = { getCertsAndProxyConfig, buildCertsAndProxyConfig };
|