Skip to content

fix: honor OS-level PAC configuration in system proxy mode#7766

Draft
pooja-bruno wants to merge 1 commit intousebruno:mainfrom
pooja-bruno:fix/system-proxy-honor-os-pac
Draft

fix: honor OS-level PAC configuration in system proxy mode#7766
pooja-bruno wants to merge 1 commit intousebruno:mainfrom
pooja-bruno:fix/system-proxy-honor-os-pac

Conversation

@pooja-bruno
Copy link
Copy Markdown
Collaborator

@pooja-bruno pooja-bruno commented Apr 15, 2026

Description

Bruno supports PAC proxy resolution when explicitly configured in preferences, but in System proxy mode, OS-level PAC configuration is silently ignored.

Contribution Checklist:

  • I've used AI significantly to create this pull request
  • The pull request only addresses one issue or adds one feature.
  • The pull request does not introduce any breaking changes
  • I have added screenshots or gifs to help explain the change if applicable.
  • I have read the contribution guidelines.
  • Create an issue and link to the pull request.

Note: Keeping the PR small and focused helps make it easier to review and merge. If you have multiple changes you want to make, please consider submitting them as separate pull requests.

Publishing to New Package Managers

Please see here for more information.

Summary by CodeRabbit

  • New Features

    • Added support for Proxy Auto-Configuration (PAC) URLs from system proxy settings on Windows, macOS, and Linux
    • System proxy detection now recognizes PAC URLs configured at the OS level and applies them to network requests
  • Tests

    • Added integration tests validating PAC proxy functionality with multiple scenarios

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 15, 2026

Walkthrough

This PR adds PAC (Proxy Auto-Config) URL support across the proxy system. It extends platform-specific proxy detectors to extract PAC URLs, updates type definitions to include pac_url fields, refactors PAC resolution logic into a reusable helper, and integrates system PAC proxy testing on macOS.

Changes

Cohort / File(s) Summary
Type Definitions
packages/bruno-requests/src/network/system-proxy/types.ts, packages/bruno-requests/src/utils/http-https-agents.ts
Added optional pac_url?: string | null field to ProxyConfiguration and SystemProxyConfig interfaces to support PAC URL configuration.
Platform-Specific Detectors
packages/bruno-requests/src/network/system-proxy/utils/windows.ts, packages/bruno-requests/src/network/system-proxy/utils/macos.ts, packages/bruno-requests/src/network/system-proxy/utils/linux.ts
Extended Windows, macOS, and Linux proxy detection to recognize and extract PAC/auto-config modes; now parses AutoConfigURL (Windows), ProxyAutoConfigURLString (macOS), and auto-mode URLs (GNOME/KDE on Linux).
Core Proxy Resolution
packages/bruno-requests/src/network/system-proxy/index.ts, packages/bruno-electron/src/store/system-proxy.js, packages/bruno-electron/src/ipc/network/cert-utils.js
Added pac_url field to returned ProxyConfiguration objects and fallback cache objects; ensures PAC URL flows through proxy config pipeline.
Proxy Utilities & Agent Setup
packages/bruno-electron/src/utils/proxy-util.js, packages/bruno-requests/src/utils/http-https-agents.ts
Introduced resolveAgentsFromPac(...) helper for unified PAC directive resolution; refactored explicit 'pac' mode and 'system' mode with PAC to delegate directive parsing to this helper; extended system-proxy mode to prefer PAC URLs when present.
Unit Tests
packages/bruno-requests/src/network/system-proxy/index.spec.js, packages/bruno-requests/src/network/system-proxy/utils/macos.spec.ts
Updated test expectations to include pac_url field in proxy configuration objects across all detection scenarios.
Integration Test Suite & Fixtures
tests/proxy/system-pac/system-pac-proxy.spec.ts, tests/proxy/system-pac/fixtures/collection/{bruno.json, direct.bru, proxied.bru}, tests/proxy/system-pac/init-user-data/preferences.json
Added end-to-end test suite validating macOS system PAC proxy behavior with both HTTP and file-based PAC URLs; includes test fixtures and collection configuration.

Sequence Diagram(s)

sequenceDiagram
    actor Client as Request Handler
    participant Detector as Platform Detector<br/>(Windows/macOS/Linux)
    participant Resolver as PAC Resolver<br/>(resolveAgentsFromPac)
    participant Agent as HTTP/HTTPS Agent
    participant Target as Target Server

    Client->>Detector: Detect system proxy config
    Detector->>Detector: Extract pac_url from OS
    Detector-->>Client: Return ProxyConfiguration{pac_url}
    
    Client->>Resolver: Check proxyMode='system' with pac_url
    alt PAC URL Present
        Resolver->>Resolver: Resolve PAC directives for request URL
        Resolver->>Resolver: Parse first directive (PROXY/SOCKS/DIRECT)
        Resolver->>Agent: Set httpAgent or httpsAgent based on directive
        Resolver-->>Client: Return agents
    else No PAC URL
        Client->>Client: Fall back to http_proxy/https_proxy logic
    end
    
    Client->>Agent: Use configured agent
    Agent->>Target: Route request (direct or via proxy)
    Target-->>Agent: Response
    Agent-->>Client: Return response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

size/XXL

Suggested reviewers

  • lohit-bruno
  • naman-bruno
  • bijin-bruno

Poem

🔗 A URL wrapped in PAC so fine,
Directives dance down the proxy line,
DIRECT or SOCKS, the choice is clear,
Platform-wise detection brings cheer! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding support for OS-level PAC configuration in system proxy mode, which is the primary objective across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
packages/bruno-electron/src/utils/proxy-util.js (1)

107-134: Inconsistent agent assignment compared to http-https-agents.ts.

This helper sets both httpAgent and httpsAgent regardless of request protocol, while the equivalent resolveAgentsFromPac in http-https-agents.ts (lines 424-437) selectively sets only the needed agent based on isHttpsRequest. This could cause unnecessary agent creation.

Consider aligning the behavior by passing isHttpsRequest and conditionally setting only the required agent.

♻️ Suggested approach
-async function resolveAgentsFromPac({ pacSource, requestUrl, requestConfig, tlsOptions, httpsAgentRequestFields, timeline, disableCache, hostname }) {
+async function resolveAgentsFromPac({ pacSource, requestUrl, requestConfig, tlsOptions, httpsAgentRequestFields, timeline, disableCache, hostname, isHttpsRequest }) {
   const resolver = await getPacResolver({ pacSource, httpsAgentRequestFields });
   const directives = await resolver.resolve(requestUrl);

   if (!directives || !directives.length) {
     return null;
   }

   const first = directives[0];

   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 });
+    if (isHttpsRequest) {
+      requestConfig.httpsAgent = getOrCreateHttpsAgent({ AgentClass: PatchedHttpsProxyAgent, options: tlsOptions, proxyUri, timeline, disableCache, hostname });
+    } else {
+      requestConfig.httpAgent = getOrCreateHttpAgent({ AgentClass: HttpProxyAgent, options: { keepAlive: true }, proxyUri, timeline, disableCache, hostname });
+    }
   } else if (/^SOCKS/i.test(first)) {
     // ... similar change for SOCKS
   }

   return directives;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-electron/src/utils/proxy-util.js` around lines 107 - 134, The
resolveAgentsFromPac function currently assigns both requestConfig.httpAgent and
requestConfig.httpsAgent unconditionally, creating unnecessary agents; update
resolveAgentsFromPac to accept an isHttpsRequest boolean (like in
http-https-agents.ts) and only call getOrCreateHttpAgent when !isHttpsRequest
and only call getOrCreateHttpsAgent/appropriate Agent when isHttpsRequest,
preserving the existing proxyUri/scheme/proto logic and using the same
AgentClass and options (HttpProxyAgent, PatchedHttpsProxyAgent, SocksProxyAgent)
so you avoid instantiating the unused agent.
packages/bruno-requests/src/network/system-proxy/utils/linux.ts (1)

90-95: Consider adding pac_url: null to manual proxy returns for type consistency.

The PAC code paths return { pac_url, ... } but the manual proxy returns here (and in getKDEProxy, parseProxyFromContent) omit pac_url. This inconsistency may cause downstream code to handle undefined vs null differently.

♻️ Suggested fix
       return {
         http_proxy,
         https_proxy,
         no_proxy: normalizeNoProxy(rawNoProxy),
+        pac_url: null,
         source: 'linux-system'
       };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-requests/src/network/system-proxy/utils/linux.ts` around lines
90 - 95, The manual proxy return objects lack a pac_url field causing type
inconsistency; update the manual-return branches in the linux-system code (the
return currently including http_proxy, https_proxy, no_proxy, source:
'linux-system') as well as corresponding manual-return spots in getKDEProxy and
parseProxyFromContent to include pac_url: null so all proxy result objects
always have a pac_url property (PAC code paths should continue to return the
actual pac_url).
packages/bruno-requests/src/network/system-proxy/utils/windows.ts (1)

220-226: Consider adding pac_url: null for consistency with the PAC code path.

Same observation as the Linux file: parseProxyString returns a ProxyConfiguration without pac_url, creating a shape inconsistency with configurations that include it.

♻️ Suggested fix
     return {
       http_proxy,
       https_proxy,
       no_proxy: bypassList && bypassList !== '(none)' ? normalizeNoProxy(bypassList) : null,
+      pac_url: null,
       source: 'windows-system'
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-requests/src/network/system-proxy/utils/windows.ts` around
lines 220 - 226, The returned ProxyConfiguration from the Windows system parser
(the object returned in the function that uses http_proxy, https_proxy and
normalizeNoProxy) is missing pac_url which causes shape inconsistency with the
PAC code path; update the returned object in the Windows parser (the same place
parseProxyString/normalizeNoProxy are used) to include pac_url: null so
ProxyConfiguration always has pac_url present.
tests/proxy/system-pac/system-pac-proxy.spec.ts (1)

25-35: Consider try/finally pattern for system state cleanup.

If startServers() succeeds but a test fails mid-execution, disableSystemPac() still runs via afterAll. However, if enableSystemPac() throws (e.g., permission denied), the system may be left in an inconsistent state. Consider wrapping enableSystemPac calls with their own error handling.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/proxy/system-pac/system-pac-proxy.spec.ts` around lines 25 - 35, The
before/after hooks should ensure enableSystemPac failures can't leave the OS
proxy set; wrap calls to enableSystemPac (and any system-modifying setup) in a
try/finally so disableSystemPac always runs on error: modify the setup where
startServers() and enableSystemPac() are invoked (referencing startServers,
enableSystemPac, disableSystemPac, stopServers) to catch exceptions from
enableSystemPac and call disableSystemPac in a finally block, and ensure
stopServers is awaited in the cleanup path as well so servers are stopped
whether setup succeeds or fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/bruno-requests/src/network/system-proxy/utils/macos.spec.ts`:
- Around line 48-49: Add a positive PAC-enabled test in macos.spec.ts that
exercises the new PAC extraction path: create a test case that stubs/mocks the
macOS system proxy output to include a PAC URL and assert that the parsed result
(the object under test that currently exposes pac_url) returns that non-null URL
(not null) and retains source: 'macos-system'; locate the spec in
packages/bruno-requests/src/network/system-proxy/utils/macos.spec.ts and add the
test alongside the existing cases that currently assert pac_url: null so it
covers the PAC-enabled branch of the parsing function that produces pac_url.

In `@tests/proxy/system-pac/system-pac-proxy.spec.ts`:
- Around line 12-20: The test hardcodes NETWORK_SERVICE = 'Wi-Fi' and calls
macOS-only networksetup without platform guarding; update the test to first skip
on non-Darwin platforms (use a platform check and test.skip) and make
NETWORK_SERVICE dynamic by detecting an active service or accepting the service
name from environment/utility instead of hardcoding; locate the NETWORK_SERVICE
constant and the helper functions enableSystemPac and disableSystemPac and
change them to either query available services (e.g., via networksetup
-listallnetworkservices) or accept a resolved service name, and ensure the
entire test is wrapped with a Darwin-only guard so networksetup is never invoked
on other OSes.

---

Nitpick comments:
In `@packages/bruno-electron/src/utils/proxy-util.js`:
- Around line 107-134: The resolveAgentsFromPac function currently assigns both
requestConfig.httpAgent and requestConfig.httpsAgent unconditionally, creating
unnecessary agents; update resolveAgentsFromPac to accept an isHttpsRequest
boolean (like in http-https-agents.ts) and only call getOrCreateHttpAgent when
!isHttpsRequest and only call getOrCreateHttpsAgent/appropriate Agent when
isHttpsRequest, preserving the existing proxyUri/scheme/proto logic and using
the same AgentClass and options (HttpProxyAgent, PatchedHttpsProxyAgent,
SocksProxyAgent) so you avoid instantiating the unused agent.

In `@packages/bruno-requests/src/network/system-proxy/utils/linux.ts`:
- Around line 90-95: The manual proxy return objects lack a pac_url field
causing type inconsistency; update the manual-return branches in the
linux-system code (the return currently including http_proxy, https_proxy,
no_proxy, source: 'linux-system') as well as corresponding manual-return spots
in getKDEProxy and parseProxyFromContent to include pac_url: null so all proxy
result objects always have a pac_url property (PAC code paths should continue to
return the actual pac_url).

In `@packages/bruno-requests/src/network/system-proxy/utils/windows.ts`:
- Around line 220-226: The returned ProxyConfiguration from the Windows system
parser (the object returned in the function that uses http_proxy, https_proxy
and normalizeNoProxy) is missing pac_url which causes shape inconsistency with
the PAC code path; update the returned object in the Windows parser (the same
place parseProxyString/normalizeNoProxy are used) to include pac_url: null so
ProxyConfiguration always has pac_url present.

In `@tests/proxy/system-pac/system-pac-proxy.spec.ts`:
- Around line 25-35: The before/after hooks should ensure enableSystemPac
failures can't leave the OS proxy set; wrap calls to enableSystemPac (and any
system-modifying setup) in a try/finally so disableSystemPac always runs on
error: modify the setup where startServers() and enableSystemPac() are invoked
(referencing startServers, enableSystemPac, disableSystemPac, stopServers) to
catch exceptions from enableSystemPac and call disableSystemPac in a finally
block, and ensure stopServers is awaited in the cleanup path as well so servers
are stopped whether setup succeeds or fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a06975e5-0a99-46af-8e95-afd91be54a47

📥 Commits

Reviewing files that changed from the base of the PR and between b733d0e and b0005ef.

📒 Files selected for processing (16)
  • packages/bruno-electron/src/ipc/network/cert-utils.js
  • packages/bruno-electron/src/store/system-proxy.js
  • packages/bruno-electron/src/utils/proxy-util.js
  • packages/bruno-requests/src/network/system-proxy/index.spec.js
  • packages/bruno-requests/src/network/system-proxy/index.ts
  • packages/bruno-requests/src/network/system-proxy/types.ts
  • packages/bruno-requests/src/network/system-proxy/utils/linux.ts
  • packages/bruno-requests/src/network/system-proxy/utils/macos.spec.ts
  • packages/bruno-requests/src/network/system-proxy/utils/macos.ts
  • packages/bruno-requests/src/network/system-proxy/utils/windows.ts
  • packages/bruno-requests/src/utils/http-https-agents.ts
  • tests/proxy/system-pac/fixtures/collection/bruno.json
  • tests/proxy/system-pac/fixtures/collection/direct.bru
  • tests/proxy/system-pac/fixtures/collection/proxied.bru
  • tests/proxy/system-pac/init-user-data/preferences.json
  • tests/proxy/system-pac/system-pac-proxy.spec.ts

Comment on lines +48 to 49
pac_url: null,
source: 'macos-system'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add one positive PAC-enabled test case.

Current updates assert pac_url: null, but the new PAC extraction path is not directly asserted with a non-null URL scenario.

Proposed test addition
 describe('scutil proxy detection', () => {
+  it('should detect PAC URL when PAC is enabled', async () => {
+    const scutilOutput = `<dictionary> {
+  ProxyAutoConfigEnable : 1
+  ProxyAutoConfigURLString : http://proxy.usebruno.com/proxy.pac
+  HTTPEnable : 0
+  HTTPSEnable : 0
+}`;
+
+    mockExecFile.mockResolvedValueOnce({ stdout: scutilOutput, stderr: '' });
+
+    const result = await detector.detect();
+
+    expect(result).toEqual({
+      http_proxy: null,
+      https_proxy: null,
+      no_proxy: null,
+      pac_url: 'http://proxy.usebruno.com/proxy.pac',
+      source: 'macos-system'
+    });
+  });

As per coding guidelines **/*.{test,spec}.{js,jsx,ts,tsx}: Add tests for any new functionality or meaningful changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-requests/src/network/system-proxy/utils/macos.spec.ts` around
lines 48 - 49, Add a positive PAC-enabled test in macos.spec.ts that exercises
the new PAC extraction path: create a test case that stubs/mocks the macOS
system proxy output to include a PAC URL and assert that the parsed result (the
object under test that currently exposes pac_url) returns that non-null URL (not
null) and retains source: 'macos-system'; locate the spec in
packages/bruno-requests/src/network/system-proxy/utils/macos.spec.ts and add the
test alongside the existing cases that currently assert pac_url: null so it
covers the PAC-enabled branch of the parsing function that produces pac_url.

Comment on lines +12 to +20
const NETWORK_SERVICE = 'Wi-Fi';

function enableSystemPac(pacUrl: string) {
execSync(`networksetup -setautoproxyurl "${NETWORK_SERVICE}" "${pacUrl}"`);
}

function disableSystemPac() {
execSync(`networksetup -setautoproxystate "${NETWORK_SERVICE}" off`);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded network service and missing platform guard.

  1. NETWORK_SERVICE = 'Wi-Fi' won't work on macOS machines using Ethernet, Thunderbolt Bridge, or other network interfaces.
  2. This test uses networksetup which is macOS-only, but there's no test.skip for non-Darwin platforms.
🛠️ Suggested improvements
+import * as os from 'os';
+
+// Skip on non-macOS platforms
+const isMacOS = os.platform() === 'darwin';
+
+/**
+ * Get the primary network service. Falls back to Wi-Fi if detection fails.
+ */
+function getPrimaryNetworkService(): string {
+  try {
+    // Get the device for the default route, then find its service name
+    const output = execSync('networksetup -listallnetworkservices', { encoding: 'utf8' });
+    const services = output.split('\n').filter((s) => s && !s.startsWith('*'));
+    // Prefer Wi-Fi or Ethernet
+    return services.find((s) => /wi-fi|ethernet/i.test(s)) || services[0] || 'Wi-Fi';
+  } catch {
+    return 'Wi-Fi';
+  }
+}
+
-const NETWORK_SERVICE = 'Wi-Fi';
+const NETWORK_SERVICE = isMacOS ? getPrimaryNetworkService() : 'Wi-Fi';

 test.describe('System Proxy with PAC', () => {
+  test.skip(!isMacOS, 'System PAC tests require macOS networksetup');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/proxy/system-pac/system-pac-proxy.spec.ts` around lines 12 - 20, The
test hardcodes NETWORK_SERVICE = 'Wi-Fi' and calls macOS-only networksetup
without platform guarding; update the test to first skip on non-Darwin platforms
(use a platform check and test.skip) and make NETWORK_SERVICE dynamic by
detecting an active service or accepting the service name from
environment/utility instead of hardcoding; locate the NETWORK_SERVICE constant
and the helper functions enableSystemPac and disableSystemPac and change them to
either query available services (e.g., via networksetup -listallnetworkservices)
or accept a resolved service name, and ensure the entire test is wrapped with a
Darwin-only guard so networksetup is never invoked on other OSes.

@pooja-bruno pooja-bruno marked this pull request as draft April 15, 2026 10:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant