Repository navigation
fix: strip only numeric API-version prefixes in Go and Rust (#57) - #58
Merged
Merged
Conversation
This was referenced Oct 7, 2026
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.
Description
Go
stripAPIVersionand Ruststrip_api_versionstripped any first path segment starting with/v, as if it were an API version. SoDELETE /volumes/containers/foowas classified as "delete containerfoo", allowed, and forwarded. The daemon runs it as a volume removal: a direct test returned404 get containers/foo: no such volume. The proxy and the daemon disagreed about which resource the request acts on.Fix: Go and Rust now strip one leading
/v<N>/or/v<N>.<M>/, with ASCII digits only. That is the rule TypeScript has used since #52 (^/v\d+(\.\d+)?/). Anything else is left as is, so non-GET requests fall to the default deny. Both fixes are short byte scans with no regex and no new dependencies. TS production code is unchanged.Closes #57
Before / after (Go and Rust router)
In Go, a real HTTP request
DELETE /v%D9%A1/containers/fooreaches the router as/v١/…, because net/http decodesr.URL.Path. Before this change Go allowed it. Now the handler returns 403 and doesn't forward the request. Rust and TS route the raw request target, so they see%D9%A1. That decoding difference already existed, isn't introduced here, and is tracked in #53.Tests (written first; RED commits
9c1a21d,d5567ec)TestRouteVersionedPaths, Rusttest_route_versioned_paths, TSroutes dotted API-version paths like the unversioned ones. It extends the TypeScript proxy denies dotted API-version paths (/v1.43/...) that the Docker CLI always sends #52 table, and the TypeScript proxy denies dotted API-version paths (/v1.43/...) that the Docker CLI always sends #52 "TS-only, do not copy as parity" rows are now ordinary shared rows./v1/,/v1.43/,/v10.0/(multi-digit major)./volumes/…,/version/…,/v1.2.3/…,/vabc/…,/v/…,/v1./…,/v١/…(non-ASCII digit), and/v1/v1.43/…(strip once).^/v[0-9.]+/,\d*, Unicode\d, and stripping more than once.deploy/test.sh, 35 → 36 checks:DELETE /volumes/containers/no-such-container→ 403. Before the fix, Go and Rust forwarded it and the daemon answered 404 (35 PASSED, 1 FAILED). TS already passed it.spec/router.qntmodels paths after the version prefix is stripped.Alternative rejected: copying the daemon's
^/v[0-9.]+/exactly. It would have meant loosening TS and reversing #52's/v1.2.3/row. The stricter rule fails closed: a form the daemon accepts but no client sends, such as/v1.45.0/, gets default-denied.Also in this PR:
c8d9e76corrects a pre-existing docs drift.go/main_test.gohas 24 tests, but the docs said 23 (Go total 102 → 103) ever since #47. It's a separate commit because it touches the same lines.Squash-merge required: the RED commits fail in Go and Rust by design.
Type of change
Implementation(s) changed
deploy/test.shcheck)Testing
make test-all):make test-integration,make test-integration-rsandmake test-integration-tseach printALL 36 TESTS PASSED. That includesGET /v1.45/_ping -> 200, which confirms nothing is now under-stripped.make verify): not run locally, since the spec is unchanged. It runs in CI.make test-specpasses all four instances (11 passing,10 passing,9 passing,7 passing; exit 0).make lint-allexits 0 (go vet,cargo check,tsc --noEmit)Checklist