Repository navigation
Conversation
shieldProxyHandler assigned r.URL.Path over the upstream URL's own Path, so any prefix configured on SOLONGATE_SHIELD_UPSTREAM or ANTHROPIC_BASE_URL (for example https://gateway.example.com/anthropic) was dropped and every proxied request went to the upstream host's root path instead of under that prefix. Targets now get the request path prefixed with the upstream path. The request path is only prefixed, never cleaned: dot segments, a trailing slash and percent-encoding are passed on exactly as the client sent them, so shield still does not rewrite what was asked for. RawPath follows the same join so escaped paths survive. Fixes solongate#331 Signed-off-by: fchengyan <3181088318@qq.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #331.
The bug
shieldProxyHandlerbuilt the upstream target by assigning the incoming request path over the upstream URL's own path:So a prefix configured on
SOLONGATE_SHIELD_UPSTREAMorANTHROPIC_BASE_URL—https://gateway.example.com/anthropic, the usual shape for a corporate proxy or a gateway that routes by path — was dropped from every forwarded request. The request went to<host>/v1/messagesinstead of<host>/anthropic/v1/messages, and the client saw whatever the upstream root path returns.RawPathhad the same problem for encoded paths.shieldPickUpstreamparses the URL and this handler is its only consumer ofPath, so nothing else in the tree depends on the old behaviour.The change
The target path is now the request path prefixed with the upstream path (
shieldJoinPath), andRawPathfollows the same join when the request carried an escaped path.The request path is prefixed, not cleaned.
shieldshould not rewrite what the client asked for, so dot segments, a trailing slash and percent-encoding all pass through untouched;path.Joinwould have resolved/v1/../xand dropped a trailing slash instead. The join handles: prefix""or"/"(old behaviour), prefix with a trailing slash (no doubled slash), a request for the upstream root (/becomes/llm/), and an empty request path.Verification
New tests in
packages/guard/shield/shield_test.go, all against a livehttptestupstream so they assert on the request the upstream really received:TestShieldProxyHandlerKeepsTheUpstreamPathPrefix— upstream URL path/gateway/anthropic, request/v1/messages?foo=bar.TestShieldProxyHandlerKeepsAnEscapedRequestPath—/v1/files/a%2Fbmust arrive as/gateway/v1/files/a%2Fb; the%2Fhas to survive the join.TestShieldProxyHandlerForwardsToTheUpstreamRootWithoutAPrefix— prefixless upstreams (""and"/") still forward/v1/messagesunchanged, so the common case does not regress.TestShieldJoinPath— 10 cases for the join itself, including the no-double-slash case, the upstream root, and dot segments left alone.Red/green with only the two handler lines reverted (the helper kept, so the package still compiles):
With the fix in place those two pass, and so do the existing
TestShieldProxyHandlerForwardsAndRedactsandTestShieldProxyHandlerSkipsRedactionForNonJSONBodies.go vet ./...inpackages/guardis clean,go vet ./shieldis clean forGOOSlinux, darwin and windows, andgo build ./...is clean.go test ./shield/on my Windows machine still reports the same 8 failures before and after the change, so none of them are regressions: sevenTestShieldLoadDLPConfig*cases setHOMEwitht.Setenv, butshared.SGDir()resolves the home directory throughos.UserHomeDir(), which on Windows returnsUSERPROFILEand ignoresHOME— the policy file the tests write is never read, so they fall back to the defaults.TestShieldRunChildReportsSignalDeathsAbove128asserts Unix signal semantics.-raceis not available on this machine (no cgo toolchain), so the race build is left to CI.One behaviour note for anyone whose upstream currently carries a path: this is what makes that prefix work, so a setup that was silently talking to the upstream root will start talking to the prefixed path it configured. I did not touch the case where the prefix and the request path are unrelated to each other, because there is no way to tell a mistake from intent there.
AI disclosure: this fix was located, reproduced and written with the help of an AI coding assistant under my direction.