GITHUB.COM/GETKIN/KIN-OPENAPI · CVE-2026-100351 · Package
FIX → 0.147.0
A flaw in the Go library kin-openapi lets a request with an encoded path be matched to the wrong operation in ValidationHandler, so the security requirement for that operation is bypassed. An attacker can reach an endpoint that should have required authentication or authorization.
first public Aug 18, 2026 · 3 sources
Check it with your coding agent
It checks your repository and tells you if you’re affected, with the fix.
Applications written in Go that import kin-openapi and use its ValidationHandler to enforce OpenAPI security requirements on incoming requests are exposed. Services that do not use this library, or that enforce authentication separately in their own code, are less likely to be affected. The affected range is 0.144.0 up to but not including 0.147.0.
Not affected if auth-enforced-elsewhere, not-using-validationhandler
Who cares api-servers, cwe-289, cwe-436, go, kin-openapi, openapi
go list -m github.com/getkin/kin-openapiUpgrade to 0.147.0
0.147.0openapi3filter.ValidationHandler.Middleware validates requests with kin-openapi's
legacy router, which selects an OpenAPI operation from the decoded URL.Path. The
method is documented as implementing gorilla/mux.MiddlewareFunc, but a downstream
gorilla/mux router configured with UseEncodedPath() selects its handler from the
encoded representation (URL.RawPath).
An unauthenticated request such as /public%2Fstatus can therefore be validated as an
intentionally public operation (/public/status) and then dispatched to a protected
operation (/{id}). The configured, rejecting AuthenticationFunc is never invoked,
and the protected downstream handler runs.
This is distinct from GHSA-r277-6w6q-xmqw. The behavior below uses an explicit authentication callback that rejects missing credentials and remains reproducible after the v0.144.0 fix for the unsafe default callback.
github.com/getkin/kin-openapiAre you affected?
Tell InfraStack what you run. It checks this one, and every next one, against your stack.
Ask from here. InfraStack checks the affected versions against your stack and answers in your inbox.
InfraStack checks the affected versions against your stack and answers in Slack, with the evidence and the fix.
openapi3filter.ValidationHandler.Middlewareopenapi3filter/, and that test passed; maintainers
should independently confirm the version ranged33e44e67734dcde81b576582185e1343fb3985dAn unauthenticated remote requester can reach a handler whose OpenAPI operation requires authentication when all of the following are true:
ValidationHandler.Middleware as the security gate;UseEncodedPath();The protected operation's path-parameter value is constrained: it is an encoded
representation that decodes to a public literal path (public%2Fstatus in this PoC),
not an arbitrary attacker-selected identifier. A protected operation without a
compatible parameterized path is not reachable through this specific mechanism.
AuthenticationFunc is never invoked.At the confirmed development revision:
openapi3filter/validation_handler.go: ValidationHandler.Load constructs
routers/legacy.NewRouter unconditionally.routers/legacy/router.go: route selection uses the decoded request path.routers/gorillamux/router.go: the kin-openapi gorilla router constructs a
gorilla/mux router with UseEncodedPath().ValidationHandler.Middleware forwards the unchanged request after validation.The component making the authentication decision and the component dispatching the handler can therefore disagree about the selected operation.
The attached PoC uses a real net/http server, an explicit API-key callback that
rejects missing credentials, and a downstream gorilla/mux router with
UseEncodedPath().
From a kin-openapi checkout, copy the self-contained attachment
encoded_path_route_confusion_test.go.txt into openapi3filter/ as
encoded_path_route_confusion_test.go, then run:
go test -v ./openapi3filter -run '^TestEncodedPathAuthorizationBypass$' -count=1
Expected attack observation:
status=202 downstream_route=/{id} protected_id=public%2Fstatus authentication_calls=0
wiring=Router.Use status=202 downstream_route=/{id} protected_id=public%2Fstatus authentication_calls=0
Controls included in the test show that:
/ordinary selects the protected operation, invokes authentication once, and is
rejected with HTTP 401;/public/status reaches only the intentionally public handler;/{id} handler with HTTP 202 while the
authentication callback remains uncalled;router.Use(validationHandler.Middleware) wiring reproduce the same result.The same request was reproduced through Nginx 1.31.3 using the official
nginx:alpine image digest
sha256:4a73073bd557c65b759505da037898b61f1be6cbcc3c2c3aeac22d2a470c1752.
The exact container invocation was:
docker run --rm --name kin-openapi-nginx \
--add-host=host.docker.internal:host-gateway \
-p 18081:8080 -p 18082:8081 \
-v "$PWD/poc/nginx.conf:/etc/nginx/nginx.conf:ro" \
nginx:alpine
Container port 8080 uses proxy_pass http://host.docker.internal:18080; without a
replacement URI. Host port 18081 therefore preserved /public%2Fstatus; the protected
handler returned HTTP 202 and the
authentication callback remained at zero calls. With
proxy_pass http://host.docker.internal:18080/; on container port 8081 / host port
18082, Nginx canonicalized the upstream URI; both layers selected /public/status and
the bypass did not occur.
Nginx access-log observations:
server_port=8080 "GET /public%2Fstatus HTTP/1.1" 202 0 "curl/8.7.1"
server_port=8081 "GET /public%2Fstatus HTTP/1.1" 204 0 "curl/8.7.1"
server_port=8080 "GET /ordinary HTTP/1.1" 401 0 "curl/8.7.1"
The response route headers were X-Poc-Route: /{id} on the preserving listener and
X-Poc-Route: /public/status on the canonicalizing listener. Backend observations for
the two requests were:
request_uri="/public%2Fstatus" path="/public/status" raw_path="/public%2Fstatus" auth_before=0
completed request_uri="/public%2Fstatus" auth_after=0
request_uri="/public/status" path="/public/status" raw_path="" auth_before=0
completed request_uri="/public/status" auth_after=0
This control shows that upstream path handling is a deployment precondition rather than claiming that every proxy configuration is affected.
The canonical protected control produced:
request_uri="/ordinary" path="/ordinary" raw_path="" auth_before=0
completed request_uri="/ordinary" auth_after=1
The authentication layer and downstream dispatcher must select the same OpenAPI operation from the same request representation.
Potential approaches:
ValidationHandler accept a caller-supplied routers.Router and require it to
match downstream routing semantics;NewValidator already accepts a router but is safe only when the
supplied router selects operations using the same semantics as the dispatcher;
callers must also supply an AuthenticationFunc through ValidationOptions, or
protected requests fail with the existing missing-authentication-service error;ValidationHandler fail closed on an encoded
path separator when URL.Path and URL.RawPath can select different operations,
and verify this behavior against the full suite;Replacing the legacy router with the kin-openapi gorilla router closes this PoC, but a local compatibility check changed two existing missing-path-parameter tests from HTTP 400 semantics to route-not-found HTTP 404 semantics. A direct router swap is therefore not a fully valida
This page as .md · .json · CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
Go Vulnerability Database, CC-BY 4.0 · NVD data is provided by NIST and is public domain · GitHub Advisory Database, CC-BY 4.0. Posts quoted belong to their authors.