Skip to content

feat(proxy): configurable intercept scope and unmatched policy#263

Open
rishabhraj36 wants to merge 4 commits into
mainfrom
260-feat-configurable-proxy-mode-intercept-scope-unmatched-policy
Open

feat(proxy): configurable intercept scope and unmatched policy#263
rishabhraj36 wants to merge 4 commits into
mainfrom
260-feat-configurable-proxy-mode-intercept-scope-unmatched-policy

Conversation

@rishabhraj36
Copy link
Copy Markdown
Collaborator

@rishabhraj36 rishabhraj36 commented May 14, 2026

Description

Introduces a single proxy.mode setting on GlobalConfig that combines two orthogonal axes into one knob:

  • Intercept scopeconnected (build the route table only from active connections, today's behavior) vs configured (build it from every configured provider, so hosts can be guarded before login).
  • Unmatched policyallow (let unmatched requests or requests with no credentials pass through untouched) vs deny (block with HTTP 403 and a proxy_deny audit event).

The four resulting modes are connected_allow (default, unchanged from today), connected_deny, configured_allow, and configured_deny.

Implementation notes:

  • New ProxyConfig Pydantic model on GlobalConfig with a Literal mode field.
  • AuthService.proxy_mode() returns the current mode; proxy_routes() switches its source set based on the scope half of the mode.
  • New GET /proxy/mode daemon endpoint + AuthsomeApiClient.proxy_mode() so the mitmproxy addon reads the mode at router-build time over the same ProxyClient protocol it already uses — no direct GlobalConfig coupling, no daemon restart required when the mode changes.
  • AuthProxyAddon now distinguishes three outcomes and emits matching audit events: proxy_inject (matched + creds available), proxy_miss / proxy_deny reason=no_match (no route), and proxy_no_credentials / proxy_deny reason=no_credentials (route matched but credential resolution failed).

Motivation and Context

Closes #260.

Today the proxy only intercepts hosts belonging to active connections and silently passes everything else through. That makes two reasonable workflows impossible:

  1. Pre-declaring guarded hosts — wanting a configured-but-not-yet-logged-in provider's hosts to be intercepted (so an unauthenticated leak doesn't bypass the proxy unnoticed).
  2. Strict egress — wanting unknown hosts (or known hosts with no credentials) blocked rather than allowed to pass through.

Folding both knobs into one explicit mode keeps the surface area small while making the intercept/block matrix obvious from a single config field.

How Has This Been Tested?

Automated:

  • uv run pytest — full suite green, including the existing tests/proxy/test_proxy.py cases (updated to mock proxy_mode()) and new coverage for connected_deny blocking unmatched hosts.
  • uv run ruff check — clean.
  • uv run ty check src/ — clean.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

The default mode (connected_allow) preserves today's behavior, so no migration is needed.

Checklist:

  • My code follows the code style of this project.
  • I have read the CONTRIBUTING document.
  • All new and existing tests passed.

@rishabhraj36 rishabhraj36 marked this pull request as ready for review May 14, 2026 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: configurable proxy mode — intercept scope × unmatched policy

1 participant