Summary
kin-openapi: ValidationHandler security-requirement bypass via encoded-path operation confusion
openapi3filter.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.
Affected products
- Product:
github.com/getkin/kin-openapi - Component:
openapi3filter.ValidationHandler.Middleware - Confirmed post-GHSA affected releases: v0.144.0, v0.145.0, and v0.146.0
- Reporter attestation: each listed release was checked out separately, the attached
single-file test was copied intoopenapi3filter/, and that test passed; maintainers
should independently confirm the version range - Confirmed affected development revision:
d33e44e67734dcde81b576582185e1343fb3985d - v0.143.0 also reproduces this mechanism, but GHSA-r277-6w6q-xmqw already provides a
simpler bypass on that release; the distinct post-fix claim starts at v0.144.0 - Earlier releases: not tested; please treat the introduction boundary as unknown
Security impact
An unauthenticated remote requester can reach a handler whose OpenAPI operation
requires authentication when all of the following are true:
- the application uses
ValidationHandler.Middlewareas the security gate; - the downstream router preserves and interprets an encoded path, such as
gorilla/mux withUseEncodedPath(); - a public exact route and a protected parameterized route overlap after decoding;
- the public and protected operations expose the same HTTP method;
- the protected handler relies on the validation layer's security decision.
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.
- Primitive: the security decision is made for a different OpenAPI operation than the
operation dispatched by the application router. - Demonstrated impact: a protected handler executes while the explicitly rejecting
AuthenticationFuncis never invoked. - Impact ceiling: confidentiality or integrity loss when the reachable protected
handler has that authority; the PoC does not demonstrate data disclosure or state
modification. - Availability impact: not demonstrated.
Root cause
At the confirmed development revision:
openapi3filter/validation_handler.go:ValidationHandler.Loadconstructsrouters/legacy.NewRouterunconditionally.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 withUseEncodedPath().ValidationHandler.Middlewareforwards the unchanged request after validation.
The component making the authentication decision and the component dispatching the
handler can therefore disagree about the selected operation.
Reproduction
The attached PoC uses a real net/http server, an explicit API-key callback that
rejects missing credentials, and a downstream gorilla/mux router withUseEncodedPath().
From a kin-openapi checkout, copy the self-contained attachmentencoded_path_route_confusion_test.go.txt into openapi3filter/ asencoded_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:
/ordinaryselects the protected operation, invokes authentication once, and is
rejected with HTTP 401;/public/statusreaches only the intentionally public handler;- the encoded request reaches the protected
/{id}handler with HTTP 202 while the
authentication callback remains uncalled; - both external wrapping and the documented
router.Use(validationHandler.Middleware)wiring reproduce the same result.
Reverse-proxy reachability
The same request was reproduced through Nginx 1.31.3 using the officialnginx:alpine image digestsha256: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. Withproxy_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 andX-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
Suggested remediation
The authentication layer and downstream dispatcher must select the same OpenAPI
operation from the same request representation.
Potential approaches:
- make
ValidationHandleraccept a caller-suppliedrouters.Routerand require it to
match downstream routing semantics; - document that
NewValidatoralready accepts a router but is safe only when the
supplied router selects operations using the same semantics as the dispatcher;
callers must also supply anAuthenticationFuncthroughValidationOptions, or
protected requests fail with the existing missing-authentication-service error; - as an interim hardening option, make
ValidationHandlerfail closed on an encoded
path separator whenURL.PathandURL.RawPathcan select different operations,
and verify this behavior against the full suite; - document the route-identity requirement and add encoded-separator regression tests.
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 validated drop-in fix.
Public-reference triage
The public advisory list and related path-encoding reports reviewed on 2026-08-18 did
not describe this authentication-boundary crossing. Relevant nearby reports include
issues #397, #1089, and #1105 and PR #400. This search is not proof of novelty; please
deduplicate against private reports.
Credit
Preferred credit: Satoru Kanno (GMO CONNECT Inc., GMO Internet Group, Inc.)
Impact
CVE-2026-100351 has a CVSS score of 7.4 (High). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (0.147.0); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.
Frequently Asked Questions
- What is CVE-2026-100351? CVE-2026-100351 is a high-severity security vulnerability in github.com/getkin/kin-openapi (go), affecting versions >= 0.144.0, < 0.147.0. It is fixed in 0.147.0.
- How severe is CVE-2026-100351? CVE-2026-100351 has a CVSS score of 7.4 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of github.com/getkin/kin-openapi are affected by CVE-2026-100351? github.com/getkin/kin-openapi (go) versions >= 0.144.0, < 0.147.0 is affected.
- Is there a fix for CVE-2026-100351? Yes. CVE-2026-100351 is fixed in 0.147.0. Upgrade to this version or later.
- Is CVE-2026-100351 exploitable, and should I be worried? Whether CVE-2026-100351 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-100351 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-100351? Upgrade
github.com/getkin/kin-openapito 0.147.0 or later.