Skip to content

Only scan, get <uuid> and vex fall back to the public proxy on 401/403; get search, apply, rollback, repair and vendor eject fail #647

Description

[agent] Filed by the scheduled architecture audit routine (CLI and core). Register: C09, C39.

Kind: bug (fixed by one refactor). Source: review Part 2.6 / R2 (C09), plus a new finding (C39): the gap is wider than the review's "get search path".

Problem

The stale-token recovery (a 401/403 from the authenticated API → rebuild the client with build_proxy_fallback_client → retry on the public proxy, free patches only) is written by each command rather than by the API client. Three commands have a copy:

Every other API consumer builds the client with get_api_client_with_overrides and has no recovery at all:

Proof by execution (main 045d7ec, each case run twice, same result). There were two local servers: an "auth" server that answers 401 to everything and a "proxy" server. The environment was SOCKET_API_TOKEN=<stale> SOCKET_ORG_SLUG=acme SOCKET_API_URL=<auth> SOCKET_PROXY_URL=<proxy>.

Command Requests seen Result
get <uuid> --json auth GET /v0/orgs/acme/patches/view/<uuid>, then proxy GET /patch/view/<uuid> warning "falling back to public patch API proxy", proxy answer used
get CVE-2021-23337 --json auth GET …/by-cve/… only {"status":"error","error":"Unauthorized: Invalid API token"}
get GHSA-… --json, get pkg:npm/lodash@4.17.20 --json auth only same error
apply --json --download-mode file (a manifest whose afterHash blob is missing) auth GET /v0/orgs/acme/patches/blob/<hash> only partialFailure, sources_download_failed, exit 1
control: the same apply with no token proxy GET /patch/blob/<hash> the request reaches the proxy

So a user whose token was revoked can scan (it falls back, and it saves free patches to the manifest), but apply on a cold blob cache, rollback and repair with the same token can't download those same free blobs, and get CVE-… fails outright.

Symptoms

None filed yet. This is a user-visible inconsistency between commands on the same credentials.

Impact: medium. A stale or revoked token in CI (a common case, since tokens rotate) breaks apply/rollback/repair/get <id>/eject even for free patches, while scan succeeds. The fallback warning text and the api_auth_fallback code are also re-spelled in each copy.

Proposed change

Move the recovery into core, so every consumer gets it and the three CLI copies are deleted:

  • Give ApiClient a one-shot downgrade. On the first is_fallback_candidate error from an authenticated call, the client swaps (once, shared across clones/tasks, for example behind an Arc<OnceLock<ApiClient>>) to the proxy client that build_proxy_fallback_client would build, records that it fell back, and retries that call on the proxy. Later calls go straight to the proxy.
  • Expose client.fell_back_to_proxy() for telemetry's fallback_to_proxy and for the paid-tier check (patch.tier == "paid" && use_public_proxy in get).
  • The warning is emitted once, through one function that renders the existing text, and vex maps it to api_auth_fallback.
  • Delete the copies in scan/mod.rs, get.rs and vex_sources.rs, including their fallback_to_proxy / use_public_proxy mutable locals where those exist only for this.

Size and scope

  • Files: api/client.rs (+80), scan/mod.rs, get.rs, vex_sources.rs (−70), and test updates. Estimated ~250 changed production lines.
  • Out of scope: the org-slug fallback route (C07, a separate decision), retry/timeout unification (C15), and hosted-bundle, which is documented as having no fallback (keep it opted out).

Acceptance criteria

  • is_fallback_candidate and build_proxy_fallback_client have no callers in crates/socket-patch-cli/src (the fallback lives only in core).
  • New wiremock tests, with auth answering 401 and the proxy serving the content: get CVE-…, get GHSA-…, get pkg:…, apply (missing blob), rollback (missing before-blob), repair and vendor eject each reach the proxy and succeed for a free patch.
  • Paid patches through a fallen-back client still report paid_required (get) and are skipped as before (scan).
  • The warning prints once per run, under the same --silent/--json gates as today.
  • The existing scan/get/vex fallback tests stay green unchanged, and so do tests/scan_api_retry_e2e.rs and crates/socket-patch-core/tests/api_retry_e2e.rs.
  • CLI_CONTRACT.md states the fallback once for every API-using command (the eject sentence becomes true).

Dependencies

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triagedarch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)bugSomething isn't workingpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions