fix: respect command ignores HTTP_PROXY#2805
Conversation
🦋 Changeset detectedLatest commit: 04e1fb7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a19b545. Configure here.
| "@redocly/cli": patch | ||
| --- | ||
|
|
||
| Fixed issue wherein `redocly respect` did not honor `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` environment variables when loading remote source descriptions or external `$ref`s. This extends the same behavior to ref resolution. |
There was a problem hiding this comment.
| Fixed issue wherein `redocly respect` did not honor `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` environment variables when loading remote source descriptions or external `$ref`s. This extends the same behavior to ref resolution. | |
| Fixed an issue where the Respect command did not honor the `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` environment variables when loading remote source descriptions or resolving external `$ref`s. Proxy settings are now consistently applied during reference resolution as well. |
| const externalRefResolver = new BaseResolver({ | ||
| http: { | ||
| headers: config.resolve?.http?.headers ?? [], | ||
| customFetch: withConnectionClient(), |
There was a problem hiding this comment.
| customFetch: withConnectionClient(), | |
| customFetch: withConnectionClient(mtlsCerts), |
| }); | ||
| }); | ||
|
|
||
| describe('withConnectionClient (proxy-only behavior)', () => { |
There was a problem hiding this comment.
| describe('withConnectionClient (proxy-only behavior)', () => { | |
| describe('withConnectionClient (proxy-only behavior)', () => { | |
| const PROXY_ENV_KEYS = [ | |
| 'HTTPS_PROXY', | |
| 'HTTP_PROXY', | |
| 'https_proxy', | |
| 'http_proxy', | |
| 'NO_PROXY', | |
| 'no_proxy', | |
| ] as const; |
| const savedEnv: Record<string, string | undefined> = {}; | ||
|
|
||
| beforeEach(() => { | ||
| savedEnv.HTTPS_PROXY = process.env.HTTPS_PROXY; |
There was a problem hiding this comment.
Can we use smth like this to save/delete envs
for (const key of PROXY_ENV_KEYS) {
savedEnv[key] = process.env[key];
delete process.env[key];
}
| }); | ||
| }); | ||
|
|
||
| describe('withConnectionClient (proxy-only behavior)', () => { |
There was a problem hiding this comment.
Maybe it will make sense to move this check into packages/cli/src/__tests__/commands/respect/respect.test.ts level and also test mTLS
Smth like this, or similar:
describe('handleRespect externalRefResolver wiring', () => {
const PROXY_ENV_KEYS = [
'HTTPS_PROXY',
'HTTP_PROXY',
'https_proxy',
'http_proxy',
'NO_PROXY',
'no_proxy',
] as const;
const savedEnv: Record<string, string | undefined> = {};
const PEM_CERT =
'-----BEGIN CERTIFICATE-----\nY2VydA==\n-----END CERTIFICATE-----';
const PEM_KEY = '-----BEGIN PRIVATE KEY-----\na2V5\n-----END PRIVATE KEY-----';
beforeEach(() => {
vi.clearAllMocks();
for (const key of PROXY_ENV_KEYS) {
savedEnv[key] = process.env[key];
delete process.env[key];
}
vi.mocked(run).mockResolvedValue([
{
hasProblems: false,
hasWarnings: false,
file: 'test.arazzo.yaml',
executedWorkflows: [],
options: {} as any,
ctx: {} as any,
totalTimeMs: 0,
totalRequests: 0,
globalTimeoutError: false,
secretValues: [],
},
]);
});
afterEach(() => {
for (const key of PROXY_ENV_KEYS) {
const value = savedEnv[key];
if (value !== undefined) {
process.env[key] = value;
} else {
delete process.env[key];
}
}
});
async function invokeHandleRespect(argvOverrides: Partial<RespectArgv> = {}) {
const config = await openapiCore.createConfig({});
await handleRespect({
argv: {
files: ['test.arazzo.yaml'],
'max-steps': 2000,
'max-fetch-timeout': 40_000,
'execution-timeout': 3_600_000,
'no-secrets-masking': false,
...argvOverrides,
} as RespectArgv,
config,
version: '1.0.0',
collectSpecData: vi.fn(),
});
const callArgs = vi.mocked(run).mock.calls[0]?.[0];
return callArgs;
}
function getResolverCustomFetch(resolver: BaseResolver | undefined) {
return (resolver as unknown as { config: { http: { customFetch?: unknown } } } | undefined)
?.config.http.customFetch;
}
it('should pass an externalRefResolver instance to run', async () => {
const callArgs = await invokeHandleRespect();
expect(callArgs?.externalRefResolver).toBeInstanceOf(BaseResolver);
});
it('should use the bare undici fetch when no proxy and no mTLS are configured', async () => {
const callArgs = await invokeHandleRespect();
expect(getResolverCustomFetch(callArgs?.externalRefResolver)).toBe(undiciFetch);
});
it('should use a proxy-aware customFetch when HTTPS_PROXY is set', async () => {
process.env.HTTPS_PROXY = 'http://proxy.local:8080';
const callArgs = await invokeHandleRespect();
const customFetch = getResolverCustomFetch(callArgs?.externalRefResolver);
expect(customFetch).not.toBe(undiciFetch);
expect(typeof customFetch).toBe('function');
});
it('should use a proxy-aware customFetch when HTTP_PROXY is set', async () => {
process.env.HTTP_PROXY = 'http://proxy.local:8080';
const callArgs = await invokeHandleRespect();
const customFetch = getResolverCustomFetch(callArgs?.externalRefResolver);
expect(customFetch).not.toBe(undiciFetch);
expect(typeof customFetch).toBe('function');
});
it('should use an mTLS-aware customFetch when argv.mtls is provided', async () => {
const callArgs = await invokeHandleRespect({
mtls: {
'https://internal-api.example.com': {
clientCert: PEM_CERT,
clientKey: PEM_KEY,
},
},
});
const customFetch = getResolverCustomFetch(callArgs?.externalRefResolver);
expect(customFetch).not.toBe(undiciFetch);
expect(typeof customFetch).toBe('function');
});
});

What/Why/How?
withConnectionClient() function in index.tsas a BaseResolver which inreturn should fetchHTTP_PROXYand other related.Reference
#2668
Testing
Screenshots (optional)
Check yourself
Security
Note
Medium Risk
Changes how
redocly respectperforms HTTP fetches by wiring proxy-aware fetching into external$refresolution; misconfiguration could affect network access in proxied environments. Scope is limited to fetch wiring and adds targeted tests.Overview
Fixes
redocly respectto honorHTTP_PROXY,HTTPS_PROXY, andNO_PROXYnot just for loading remote source descriptions but also when resolving external$refs, by providing a proxy-awareBaseResolver(externalRefResolver) configured withwithConnectionClient().Extends tests to assert
withConnectionClient()returns the bare Undicifetchwhen no proxy/certs are set, and a wrapped fetch when proxy env vars are present, and adds a changeset for a patch release.Reviewed by Cursor Bugbot for commit 04e1fb7. Bugbot is set up for automated code reviews on this repo. Configure here.