Skip to content

[PR] fix: refuse origin-form non-model paths locally instead of splicing them onto the upstream (#5211) - #5258

Open
ranxianglei wants to merge 10 commits into
masterfrom
2026-10-31_refuse-foreign-origin-form-paths
Open

ranxianglei wants to merge 10 commits into
masterfrom
2026-10-31_refuse-foreign-origin-form-paths

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

(ai: vllm-qwen/qwen3.8-27b)

Closes #5211

What this fixes

Field report (#5211, bili 0.1.208 / DSH desktop): the host's LOCAL private request POST http://127.0.0.1:<port>/api/dsh-im/delivery/messages appeared in bili.log forwarded to https://api.anthropic.com/api/dsh-im/delivery/messages — the caller's private payload left the machine to a third-party API.

Root cause (server-side). When a client's base URL points at the proxy port itself (origin-form request), resolveUpstream returns undefined and forwardUpstreamUrl splices the bare path onto opts.upstream — the configured upstream, default api.anthropic.com. That splice had NO shape check: any path (model endpoints, discovery, or an unrelated local/private API) was relayed verbatim to it (the #1284 verbatim-relay branch makes the non-conversation forwarding explicit). The in-process client over-claim (isModelApiUrl matches any trailing /messages) is a real but separate defect class — on the dsh lane the attribution gate keeps such calls direct-sent or loopback-bound, so it did not produce the reported egress. The egress producer is the server-side catch-all splice: any process pointed at the proxy port could ship its payload to the default upstream.

Fix

handle() now refuses origin-form requests whose path is neither model traffic nor a declared destination locally with 404 (error.type = unknown_upstream_path; the message tells the caller how to reach model traffic) before anything is forwarded:

No new config keys (the issue's suggested relayUnknownUpstream toggle is not implemented — the refusal is safe-default boundary behavior, not a preference knob; anyone who genuinely needs the old behavior for one endpoint declares it under providers.*). No client-side blanket loopback passthrough either — that would break compression for local model servers (Ollama/vLLM/LM Studio on loopback/LAN are supported destinations).

Behavior changes (old → new)

Surface Old New Why
Origin-form request to a path that is neither model-shaped nor declared (e.g. /api/dsh-im/delivery/messages, /v1/files, /v1/embeddings, prefixed bases like /x/v1/messages) Spliced onto the configured upstream and forwarded verbatim (default: api.anthropic.com) — payload leaves the machine Local 404 unknown_upstream_path; payload never forwarded Security boundary: a compression proxy must not act as an open relay to its configured upstream (#5211)
Origin-form model-shaped paths, /models discovery, all /bili/ · absolute-form · MITM forms, passthrough marks/hops/mode forwarded byte-identical pinned by the new suite's controls
tests/issue-393-registry-window.test.ts unknown-path control asserted an "unrecognized path" warn after forwarding asserts the 404 refusal + "refused foreign path" log pins the new boundary

Verification

  • New regression suite tests/issue5211-foreign-path-refusal.test.ts (42 tests): the exact issue repro (delivery body never reaches the mock upstream), foreign GETs, stats/admin surface, model-traffic controls with hop markers (anthropic / openai / discovery / vertex), and every escape hatch.
  • Targeted suites green locally: issue5211, issue-393, provider-protocol, dump-observability, forward-absolute-url, issue4501-pathstyle-config, fix-1117-passthrough, issue1284-misrouted-messages (83 tests in the batch); npm run typecheck clean. Full-suite verdict: remote CI on this head.

…hem onto the upstream (#5211)

An origin-form request (client base URL = the proxy port itself) whose path is neither a model API shape nor a declared destination used to be spliced onto opts.upstream by forwardUpstreamUrl and forwarded verbatim — shipping a mistargeted caller's private payload (e.g. dsh-im's local /api/dsh-im/delivery/messages body) to the configured upstream (default api.anthropic.com), out of the machine (#5211).

handle() now refuses such requests locally with a 404 (unknown_upstream_path) before any forward. Model-shaped paths (protocol-table shapes root-anchored, Google/Vertex model-in-path shapes, /models discovery) keep forwarding byte-identically; escape hatches unchanged: /bili/<full-url> tunnels, providers.* declarations for the destination, x-bili-passthrough marks, x-bili-hop chains, global passthrough mode.

Refusals are rate-limited logged per path (warn x3 + info) and surfaced at /__bili/stats (refusedForeignPaths) and the /acp status report, mirroring the #1290 unrecognizedPaths surface.

Regression suite: tests/issue5211-foreign-path-refusal.test.ts (shape-predicate unit table + e2e repro and controls); the #393 unknown-path control now asserts the new refusal.
@ranxianglei ranxianglei changed the title fix: refuse origin-form non-model paths locally instead of splicing them onto the upstream (#5211) [PR] fix: refuse origin-form non-model paths locally instead of splicing them onto the upstream (#5211) Oct 30, 2026
@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: 2026-10-31_refuse-foreign-origin-form-paths (75d728b)

Latest build — install this exact version

npm install -g billion-context@0.0.7431-pr.5258.e3680ab

This exact version is immutable and reproducible. The rolling pr tag tracks the newest test build across all PRs.

Recent builds (commit → version)

commit version build
75d728b billion-context@0.0.7431-pr.5258.e3680ab build
d758fdd billion-context@0.0.7310-pr.5258.6a5a150 build
d758d0f billion-context@0.0.7275-pr.5258.7fe1963 build
71714a1 billion-context@0.0.7271-pr.5258.7317c0b build
904a2a9 billion-context@0.0.7260-pr.5258.e35ad9b build
6570505 billion-context@0.0.7236-pr.5258.0a0afaa build
5a3b674 billion-context@0.0.7198-pr.5258.f4f4a73 build

Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr5258.tgz
npm install -g package

Install instructions are refreshed on each push.

ci-check added 2 commits October 31, 2026 05:11
…rm gate

The origin-form model-path gate (isModelTrafficOriginFormPath) admits the
protocol table's root-anchored shapes but missed Anthropic's counting
endpoint /v1/messages/count_tokens — which the pipeline itself treats as
first-class model traffic via isCountTokensRequest. An origin-form request
to that path was therefore refused locally with 404 while its sibling
/v1/messages still forwarded, silently breaking token-counting for clients
whose base URL points at the proxy port. Google's :countTokens arm was
already covered via googlePathKind; this adds the Anthropic snake_case arm.

Pinned with two positive rows in the shape table. Verified the rest of the
allow/refuse classification is byte-stable (no other shape changes).
@ranxianglei ranxianglei added bug Something isn't working p2 scope/wiring labels Oct 30, 2026

This branch has not been deployed

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

Labels

bug Something isn't working p2 scope/wiring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant