mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-24 05:05:39 +00:00
fix: cli proxy config updates (#6846)
* fix: cli `proxy config` updates * fix: review comment fixes
This commit is contained in:
@@ -22,7 +22,7 @@ const { createFormData } = require('../utils/form-data');
|
||||
const protocolRegex = /^([-+\w]{1,25})(:?\/\/|:)/;
|
||||
const { NtlmClient } = require('axios-ntlm');
|
||||
const { addDigestInterceptor } = require('@usebruno/requests');
|
||||
const { getCACertificates } = require('@usebruno/requests');
|
||||
const { getCACertificates, transformProxyConfig } = require('@usebruno/requests');
|
||||
const { getOAuth2Token } = require('../utils/oauth2');
|
||||
const { encodeUrl, buildFormUrlEncodedPayload, extractPromptVariables, isFormData } = require('@usebruno/common').utils;
|
||||
|
||||
@@ -288,25 +288,27 @@ const runSingleRequest = async function (
|
||||
let proxyMode = 'off';
|
||||
let proxyConfig = {};
|
||||
|
||||
const collectionProxyConfig = get(brunoConfig, 'proxy', {});
|
||||
const collectionProxyEnabled = get(collectionProxyConfig, 'enabled', false);
|
||||
const collectionProxyConfig = transformProxyConfig(get(brunoConfig, 'proxy', {}));
|
||||
const collectionProxyDisabled = get(collectionProxyConfig, 'disabled', false);
|
||||
const collectionProxyInherit = get(collectionProxyConfig, 'inherit', true);
|
||||
const collectionProxyConfigData = get(collectionProxyConfig, 'config', {});
|
||||
|
||||
if (noproxy) {
|
||||
// If noproxy flag is set, don't use any proxy
|
||||
if (noproxy || collectionProxyDisabled) {
|
||||
// If noproxy flag is set or collection proxy is disabled, don't use any proxy
|
||||
proxyMode = 'off';
|
||||
} else if (collectionProxyEnabled === true) {
|
||||
// If collection proxy is enabled, use it
|
||||
proxyConfig = collectionProxyConfig;
|
||||
} else if (!collectionProxyDisabled && !collectionProxyInherit) {
|
||||
// Use collection-specific proxy
|
||||
proxyConfig = collectionProxyConfigData;
|
||||
proxyMode = 'on';
|
||||
} else if (collectionProxyEnabled === 'global') {
|
||||
// If collection proxy is set to 'global', use system proxy
|
||||
} else if (!collectionProxyDisabled && collectionProxyInherit) {
|
||||
// Inherit from system proxy
|
||||
const { http_proxy, https_proxy } = getSystemProxyEnvVariables();
|
||||
if (http_proxy?.length || https_proxy?.length) {
|
||||
proxyMode = 'system';
|
||||
}
|
||||
} else {
|
||||
proxyMode = 'off';
|
||||
// else: no system proxy available, proxyMode stays 'off'
|
||||
}
|
||||
// else: collection proxy is disabled, proxyMode stays 'off'
|
||||
|
||||
if (proxyMode === 'on') {
|
||||
const shouldProxy = shouldUseProxy(request.url, get(proxyConfig, 'bypassProxy', ''));
|
||||
@@ -314,7 +316,7 @@ const runSingleRequest = async function (
|
||||
const proxyProtocol = interpolateString(get(proxyConfig, 'protocol'), interpolationOptions);
|
||||
const proxyHostname = interpolateString(get(proxyConfig, 'hostname'), interpolationOptions);
|
||||
const proxyPort = interpolateString(get(proxyConfig, 'port'), interpolationOptions);
|
||||
const proxyAuthEnabled = get(proxyConfig, 'auth.enabled', false);
|
||||
const proxyAuthEnabled = !get(proxyConfig, 'auth.disabled', false);
|
||||
const socksEnabled = proxyProtocol.includes('socks');
|
||||
let uriPort = isUndefined(proxyPort) || isNull(proxyPort) ? '' : `:${proxyPort}`;
|
||||
let proxyUri;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const path = require('path');
|
||||
const { isFile, isDirectory } = require('./filesystem');
|
||||
const { get } = require('lodash');
|
||||
const { transformProxyConfig } = require('@usebruno/requests');
|
||||
|
||||
function transformBrunoConfigBeforeSave(brunoConfig) {
|
||||
// remove exists from importPaths and protoFiles
|
||||
@@ -76,55 +76,7 @@ async function transformBrunoConfigAfterRead(brunoConfig, collectionPathname) {
|
||||
|
||||
// Migrate proxy configuration from old format to new format
|
||||
if (brunoConfig.proxy) {
|
||||
const proxy = brunoConfig.proxy || {};
|
||||
|
||||
// Check if this is an old format (has 'enabled' property)
|
||||
if (proxy.hasOwnProperty('enabled')) {
|
||||
const enabled = proxy.enabled;
|
||||
|
||||
let newProxy = {
|
||||
inherit: true,
|
||||
config: {
|
||||
protocol: proxy.protocol || 'http',
|
||||
hostname: proxy.hostname || '',
|
||||
port: proxy.port || null,
|
||||
auth: {
|
||||
username: get(proxy, 'auth.username', ''),
|
||||
password: get(proxy, 'auth.password', '')
|
||||
},
|
||||
bypassProxy: proxy.bypassProxy || ''
|
||||
}
|
||||
};
|
||||
|
||||
// Handle old format: enabled (true | false | 'global')
|
||||
if (enabled === true) {
|
||||
newProxy.disabled = false;
|
||||
newProxy.inherit = false;
|
||||
} else if (enabled === false) {
|
||||
newProxy.disabled = true;
|
||||
newProxy.inherit = false;
|
||||
} else if (enabled === 'global') {
|
||||
newProxy.disabled = false;
|
||||
newProxy.inherit = true;
|
||||
}
|
||||
|
||||
// Migrate auth.enabled to auth.disabled
|
||||
if (get(proxy, 'auth.enabled') === false) {
|
||||
newProxy.config.auth.disabled = true;
|
||||
}
|
||||
// If auth.enabled is true or undefined, omit disabled (defaults to false)
|
||||
|
||||
// Omit disabled: false at top level (optional field)
|
||||
if (newProxy.disabled === false) {
|
||||
delete newProxy.disabled;
|
||||
}
|
||||
// Omit auth.disabled: false (optional field)
|
||||
if (newProxy.config.auth.disabled === false) {
|
||||
delete newProxy.config.auth.disabled;
|
||||
}
|
||||
|
||||
brunoConfig.proxy = newProxy;
|
||||
}
|
||||
brunoConfig.proxy = transformProxyConfig(brunoConfig.proxy);
|
||||
}
|
||||
|
||||
return brunoConfig;
|
||||
|
||||
@@ -4,6 +4,7 @@ export { WsClient } from './ws/ws-client';
|
||||
export { default as cookies } from './cookies';
|
||||
|
||||
export { getCACertificates } from './utils/ca-cert';
|
||||
export { transformProxyConfig } from './utils/proxy-util';
|
||||
export { default as createVaultClient, VaultError } from './utils/node-vault';
|
||||
export type { VaultClient, VaultConfig, VaultRequestOptions } from './utils/node-vault';
|
||||
|
||||
|
||||
336
packages/bruno-requests/src/utils/proxy-util.spec.ts
Normal file
336
packages/bruno-requests/src/utils/proxy-util.spec.ts
Normal file
@@ -0,0 +1,336 @@
|
||||
import { transformProxyConfig } from './proxy-util';
|
||||
|
||||
describe('transformProxyConfig', () => {
|
||||
describe('Migration from old to new format', () => {
|
||||
describe('Old Format: enabled (true | false | "global")', () => {
|
||||
test('should migrate enabled: true to disabled: false, inherit: false', () => {
|
||||
const oldConfig = {
|
||||
enabled: true,
|
||||
protocol: 'http',
|
||||
hostname: 'proxy.example.com',
|
||||
port: 8080,
|
||||
auth: {
|
||||
enabled: true,
|
||||
username: 'user',
|
||||
password: 'pass'
|
||||
},
|
||||
bypassProxy: 'localhost'
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(oldConfig);
|
||||
|
||||
expect(result).toEqual({
|
||||
inherit: false,
|
||||
config: {
|
||||
protocol: 'http',
|
||||
hostname: 'proxy.example.com',
|
||||
port: 8080,
|
||||
auth: {
|
||||
username: 'user',
|
||||
password: 'pass'
|
||||
},
|
||||
bypassProxy: 'localhost'
|
||||
}
|
||||
});
|
||||
expect((result as any).disabled).toBeUndefined(); // disabled: false is omitted
|
||||
});
|
||||
|
||||
test('should migrate enabled: false to disabled: true, inherit: false', () => {
|
||||
const oldConfig = {
|
||||
enabled: false,
|
||||
protocol: 'http',
|
||||
hostname: 'proxy.example.com',
|
||||
port: 8080,
|
||||
auth: {
|
||||
enabled: false,
|
||||
username: '',
|
||||
password: ''
|
||||
},
|
||||
bypassProxy: ''
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(oldConfig);
|
||||
|
||||
expect((result as any).disabled).toBe(true);
|
||||
expect((result as any).inherit).toBe(false);
|
||||
});
|
||||
|
||||
test('should migrate enabled: "global" to disabled: false, inherit: true', () => {
|
||||
const oldConfig = {
|
||||
enabled: 'global' as const,
|
||||
protocol: 'http',
|
||||
hostname: '',
|
||||
port: null,
|
||||
auth: {
|
||||
enabled: false,
|
||||
username: '',
|
||||
password: ''
|
||||
},
|
||||
bypassProxy: ''
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(oldConfig);
|
||||
|
||||
expect((result as any).disabled).toBeUndefined(); // disabled: false is omitted
|
||||
expect((result as any).inherit).toBe(true);
|
||||
});
|
||||
|
||||
test('should migrate auth.enabled: false to auth.disabled: true', () => {
|
||||
const oldConfig = {
|
||||
enabled: true,
|
||||
protocol: 'http',
|
||||
hostname: 'proxy.example.com',
|
||||
port: 8080,
|
||||
auth: {
|
||||
enabled: false,
|
||||
username: 'user',
|
||||
password: 'pass'
|
||||
},
|
||||
bypassProxy: ''
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(oldConfig);
|
||||
|
||||
expect((result as any).config.auth.disabled).toBe(true);
|
||||
expect((result as any).config.auth.username).toBe('user');
|
||||
expect((result as any).config.auth.password).toBe('pass');
|
||||
});
|
||||
|
||||
test('should omit auth.disabled when auth.enabled: true', () => {
|
||||
const oldConfig = {
|
||||
enabled: true,
|
||||
protocol: 'http',
|
||||
hostname: 'proxy.example.com',
|
||||
port: 8080,
|
||||
auth: {
|
||||
enabled: true,
|
||||
username: 'user',
|
||||
password: 'pass'
|
||||
},
|
||||
bypassProxy: ''
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(oldConfig);
|
||||
|
||||
expect((result as any).config.auth.disabled).toBeUndefined();
|
||||
expect((result as any).config.auth.username).toBe('user');
|
||||
expect((result as any).config.auth.password).toBe('pass');
|
||||
});
|
||||
});
|
||||
|
||||
describe('New Format (no migration)', () => {
|
||||
test('should not modify new format with inherit: false', () => {
|
||||
const newConfig = {
|
||||
inherit: false,
|
||||
config: {
|
||||
protocol: 'https',
|
||||
hostname: 'proxy.example.com',
|
||||
port: 8443,
|
||||
auth: {
|
||||
username: 'user',
|
||||
password: 'pass'
|
||||
},
|
||||
bypassProxy: '*.local'
|
||||
}
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(newConfig);
|
||||
|
||||
expect(result).toEqual(newConfig);
|
||||
});
|
||||
|
||||
test('should not modify new format with inherit: true', () => {
|
||||
const newConfig = {
|
||||
inherit: true,
|
||||
config: {
|
||||
protocol: 'http',
|
||||
hostname: '',
|
||||
port: null,
|
||||
auth: {
|
||||
username: '',
|
||||
password: ''
|
||||
},
|
||||
bypassProxy: ''
|
||||
}
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(newConfig);
|
||||
|
||||
expect(result).toEqual(newConfig);
|
||||
});
|
||||
|
||||
test('should not modify new format with disabled: true', () => {
|
||||
const newConfig = {
|
||||
disabled: true,
|
||||
inherit: false,
|
||||
config: {
|
||||
protocol: 'http',
|
||||
hostname: '',
|
||||
port: null,
|
||||
auth: {
|
||||
username: '',
|
||||
password: ''
|
||||
},
|
||||
bypassProxy: ''
|
||||
}
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(newConfig);
|
||||
|
||||
expect(result).toEqual(newConfig);
|
||||
});
|
||||
|
||||
test('should not modify new format with auth.disabled: true', () => {
|
||||
const newConfig = {
|
||||
inherit: false,
|
||||
config: {
|
||||
protocol: 'http',
|
||||
hostname: 'proxy.example.com',
|
||||
port: 8080,
|
||||
auth: {
|
||||
disabled: true,
|
||||
username: 'user',
|
||||
password: 'pass'
|
||||
},
|
||||
bypassProxy: ''
|
||||
}
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(newConfig);
|
||||
|
||||
expect(result).toEqual(newConfig);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
test('should handle missing/null/undefined proxy config', () => {
|
||||
expect(transformProxyConfig(null)).toEqual({});
|
||||
expect(transformProxyConfig(undefined)).toEqual({});
|
||||
expect(transformProxyConfig({})).toEqual({});
|
||||
});
|
||||
|
||||
test('should handle null port values', () => {
|
||||
const oldConfig = {
|
||||
enabled: true,
|
||||
protocol: 'http',
|
||||
hostname: 'proxy.example.com',
|
||||
port: null,
|
||||
auth: {
|
||||
enabled: false,
|
||||
username: '',
|
||||
password: ''
|
||||
},
|
||||
bypassProxy: ''
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(oldConfig);
|
||||
|
||||
expect((result as any).config.port).toBeNull();
|
||||
});
|
||||
|
||||
test('should handle SOCKS protocols', () => {
|
||||
const oldConfig = {
|
||||
enabled: true,
|
||||
protocol: 'socks5',
|
||||
hostname: 'socks.example.com',
|
||||
port: 1080,
|
||||
auth: {
|
||||
enabled: true,
|
||||
username: 'socksuser',
|
||||
password: 'sockspass'
|
||||
},
|
||||
bypassProxy: ''
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(oldConfig);
|
||||
|
||||
expect((result as any).config.protocol).toBe('socks5');
|
||||
expect((result as any).config.hostname).toBe('socks.example.com');
|
||||
expect((result as any).config.port).toBe(1080);
|
||||
});
|
||||
|
||||
test('should handle missing auth object', () => {
|
||||
const oldConfig = {
|
||||
enabled: true,
|
||||
protocol: 'http',
|
||||
hostname: 'proxy.example.com',
|
||||
port: 8080,
|
||||
bypassProxy: ''
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(oldConfig);
|
||||
|
||||
expect((result as any).config.auth).toEqual({
|
||||
username: '',
|
||||
password: ''
|
||||
});
|
||||
});
|
||||
|
||||
test('should handle missing protocol (defaults to http)', () => {
|
||||
const oldConfig = {
|
||||
enabled: true,
|
||||
hostname: 'proxy.example.com',
|
||||
port: 8080
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(oldConfig);
|
||||
|
||||
expect((result as any).config.protocol).toBe('http');
|
||||
});
|
||||
|
||||
test('should handle missing hostname (defaults to empty string)', () => {
|
||||
const oldConfig = {
|
||||
enabled: true,
|
||||
protocol: 'http',
|
||||
port: 8080
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(oldConfig);
|
||||
|
||||
expect((result as any).config.hostname).toBe('');
|
||||
});
|
||||
|
||||
test('should handle missing port (defaults to null)', () => {
|
||||
const oldConfig = {
|
||||
enabled: true,
|
||||
protocol: 'http',
|
||||
hostname: 'proxy.example.com'
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(oldConfig);
|
||||
|
||||
expect((result as any).config.port).toBeNull();
|
||||
});
|
||||
|
||||
test('should handle missing bypassProxy (defaults to empty string)', () => {
|
||||
const oldConfig = {
|
||||
enabled: true,
|
||||
protocol: 'http',
|
||||
hostname: 'proxy.example.com',
|
||||
port: 8080
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(oldConfig);
|
||||
|
||||
expect((result as any).config.bypassProxy).toBe('');
|
||||
});
|
||||
|
||||
test('should handle auth with missing username/password', () => {
|
||||
const oldConfig = {
|
||||
enabled: true,
|
||||
protocol: 'http',
|
||||
hostname: 'proxy.example.com',
|
||||
port: 8080,
|
||||
auth: {
|
||||
enabled: true
|
||||
}
|
||||
};
|
||||
|
||||
const result = transformProxyConfig(oldConfig);
|
||||
|
||||
expect((result as any).config.auth.username).toBe('');
|
||||
expect((result as any).config.auth.password).toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
92
packages/bruno-requests/src/utils/proxy-util.ts
Normal file
92
packages/bruno-requests/src/utils/proxy-util.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Transform proxy config from old format to new format.
|
||||
* Old format: { enabled: true | false | 'global', protocol, hostname, port, auth: { enabled, ... }, ... }
|
||||
* New format: { disabled?, inherit, config: { protocol, hostname, port, auth: { disabled?, ... }, ... } }
|
||||
*/
|
||||
|
||||
interface OldProxyAuth {
|
||||
enabled?: boolean;
|
||||
username?: string;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
interface OldProxyConfig {
|
||||
enabled?: true | false | 'global';
|
||||
protocol?: string;
|
||||
hostname?: string;
|
||||
port?: number | null;
|
||||
auth?: OldProxyAuth;
|
||||
bypassProxy?: string;
|
||||
}
|
||||
|
||||
interface NewProxyAuth {
|
||||
disabled?: boolean;
|
||||
username?: string;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
interface NewProxyConfig {
|
||||
disabled?: boolean;
|
||||
inherit: boolean;
|
||||
config: {
|
||||
protocol: string;
|
||||
hostname: string;
|
||||
port: number | null;
|
||||
auth: NewProxyAuth;
|
||||
bypassProxy: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const transformProxyConfig = (proxy: OldProxyConfig | NewProxyConfig | null | undefined): NewProxyConfig | OldProxyConfig => {
|
||||
proxy = proxy || {};
|
||||
// Check if this is an old format (has 'enabled' property)
|
||||
if (proxy.hasOwnProperty('enabled')) {
|
||||
const oldProxy = proxy as OldProxyConfig;
|
||||
const enabled = oldProxy.enabled;
|
||||
|
||||
const newProxy: NewProxyConfig = {
|
||||
inherit: true,
|
||||
config: {
|
||||
protocol: oldProxy.protocol || 'http',
|
||||
hostname: oldProxy.hostname || '',
|
||||
port: oldProxy.port || null,
|
||||
auth: {
|
||||
username: oldProxy.auth?.username || '',
|
||||
password: oldProxy.auth?.password || ''
|
||||
},
|
||||
bypassProxy: oldProxy.bypassProxy || ''
|
||||
}
|
||||
};
|
||||
|
||||
// Handle old format: enabled (true | false | 'global')
|
||||
if (enabled === true) {
|
||||
newProxy.disabled = false;
|
||||
newProxy.inherit = false;
|
||||
} else if (enabled === false) {
|
||||
newProxy.disabled = true;
|
||||
newProxy.inherit = false;
|
||||
} else if (enabled === 'global') {
|
||||
newProxy.disabled = false;
|
||||
newProxy.inherit = true;
|
||||
}
|
||||
|
||||
// Migrate auth.enabled to auth.disabled
|
||||
if (oldProxy.auth?.enabled === false) {
|
||||
newProxy.config.auth.disabled = true;
|
||||
}
|
||||
// If auth.enabled is true or undefined, omit disabled (defaults to false)
|
||||
|
||||
// Omit disabled: false at top level (optional field)
|
||||
if (newProxy.disabled === false) {
|
||||
delete newProxy.disabled;
|
||||
}
|
||||
// Omit auth.disabled: false (optional field)
|
||||
if (newProxy.config.auth.disabled === false) {
|
||||
delete newProxy.config.auth.disabled;
|
||||
}
|
||||
|
||||
return newProxy;
|
||||
}
|
||||
|
||||
return proxy;
|
||||
};
|
||||
Reference in New Issue
Block a user