mirror of
https://github.com/golang/go.git
synced 2025-10-19 11:03:18 +00:00
[release-branch.go1.25] net/http: require exact match for CrossSiteProtection bypass patterns
Fixes #75160 Updates #75054 Fixes CVE-2025-47910 Change-Id: I6a6a696440c45c450d2cd681f418b01aa0422a60 Reviewed-on: https://go-review.googlesource.com/c/go/+/699276 Reviewed-by: Roland Shoemaker <roland@golang.org> Reviewed-by: Cherry Mui <cherryyz@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
cdd8cf4988
commit
b1959cf6f7
2 changed files with 31 additions and 8 deletions
|
@ -77,13 +77,21 @@ func (c *CrossOriginProtection) AddTrustedOrigin(origin string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var noopHandler = HandlerFunc(func(w ResponseWriter, r *Request) {})
|
type noopHandler struct{}
|
||||||
|
|
||||||
|
func (noopHandler) ServeHTTP(ResponseWriter, *Request) {}
|
||||||
|
|
||||||
|
var sentinelHandler Handler = &noopHandler{}
|
||||||
|
|
||||||
// AddInsecureBypassPattern permits all requests that match the given pattern.
|
// AddInsecureBypassPattern permits all requests that match the given pattern.
|
||||||
// The pattern syntax and precedence rules are the same as [ServeMux].
|
|
||||||
//
|
//
|
||||||
// AddInsecureBypassPattern can be called concurrently with other methods
|
// The pattern syntax and precedence rules are the same as [ServeMux]. Only
|
||||||
// or request handling, and applies to future requests.
|
// requests that match the pattern directly are permitted. Those that ServeMux
|
||||||
|
// would redirect to a pattern (e.g. after cleaning the path or adding a
|
||||||
|
// trailing slash) are not.
|
||||||
|
//
|
||||||
|
// AddInsecureBypassPattern can be called concurrently with other methods or
|
||||||
|
// request handling, and applies to future requests.
|
||||||
func (c *CrossOriginProtection) AddInsecureBypassPattern(pattern string) {
|
func (c *CrossOriginProtection) AddInsecureBypassPattern(pattern string) {
|
||||||
var bypass *ServeMux
|
var bypass *ServeMux
|
||||||
|
|
||||||
|
@ -99,7 +107,7 @@ func (c *CrossOriginProtection) AddInsecureBypassPattern(pattern string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bypass.Handle(pattern, noopHandler)
|
bypass.Handle(pattern, sentinelHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDenyHandler sets a handler to invoke when a request is rejected.
|
// SetDenyHandler sets a handler to invoke when a request is rejected.
|
||||||
|
@ -172,7 +180,7 @@ var (
|
||||||
// be deferred until the last moment.
|
// be deferred until the last moment.
|
||||||
func (c *CrossOriginProtection) isRequestExempt(req *Request) bool {
|
func (c *CrossOriginProtection) isRequestExempt(req *Request) bool {
|
||||||
if bypass := c.bypass.Load(); bypass != nil {
|
if bypass := c.bypass.Load(); bypass != nil {
|
||||||
if _, pattern := bypass.Handler(req); pattern != "" {
|
if h, _ := bypass.Handler(req); h == sentinelHandler {
|
||||||
// The request matches a bypass pattern.
|
// The request matches a bypass pattern.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,11 @@ func TestCrossOriginProtectionPatternBypass(t *testing.T) {
|
||||||
protection := http.NewCrossOriginProtection()
|
protection := http.NewCrossOriginProtection()
|
||||||
protection.AddInsecureBypassPattern("/bypass/")
|
protection.AddInsecureBypassPattern("/bypass/")
|
||||||
protection.AddInsecureBypassPattern("/only/{foo}")
|
protection.AddInsecureBypassPattern("/only/{foo}")
|
||||||
|
protection.AddInsecureBypassPattern("/no-trailing")
|
||||||
|
protection.AddInsecureBypassPattern("/yes-trailing/")
|
||||||
|
protection.AddInsecureBypassPattern("PUT /put-only/")
|
||||||
|
protection.AddInsecureBypassPattern("GET /get-only/")
|
||||||
|
protection.AddInsecureBypassPattern("POST /post-only/")
|
||||||
handler := protection.Handler(okHandler)
|
handler := protection.Handler(okHandler)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
@ -126,13 +131,23 @@ func TestCrossOriginProtectionPatternBypass(t *testing.T) {
|
||||||
{"non-bypass path without sec-fetch-site", "/api/", "", http.StatusForbidden},
|
{"non-bypass path without sec-fetch-site", "/api/", "", http.StatusForbidden},
|
||||||
{"non-bypass path with cross-site", "/api/", "cross-site", http.StatusForbidden},
|
{"non-bypass path with cross-site", "/api/", "cross-site", http.StatusForbidden},
|
||||||
|
|
||||||
{"redirect to bypass path without ..", "/foo/../bypass/bar", "", http.StatusOK},
|
{"redirect to bypass path without ..", "/foo/../bypass/bar", "", http.StatusForbidden},
|
||||||
{"redirect to bypass path with trailing slash", "/bypass", "", http.StatusOK},
|
{"redirect to bypass path with trailing slash", "/bypass", "", http.StatusForbidden},
|
||||||
{"redirect to non-bypass path with ..", "/foo/../api/bar", "", http.StatusForbidden},
|
{"redirect to non-bypass path with ..", "/foo/../api/bar", "", http.StatusForbidden},
|
||||||
{"redirect to non-bypass path with trailing slash", "/api", "", http.StatusForbidden},
|
{"redirect to non-bypass path with trailing slash", "/api", "", http.StatusForbidden},
|
||||||
|
|
||||||
{"wildcard bypass", "/only/123", "", http.StatusOK},
|
{"wildcard bypass", "/only/123", "", http.StatusOK},
|
||||||
{"non-wildcard", "/only/123/foo", "", http.StatusForbidden},
|
{"non-wildcard", "/only/123/foo", "", http.StatusForbidden},
|
||||||
|
|
||||||
|
// https://go.dev/issue/75054
|
||||||
|
{"no trailing slash exact match", "/no-trailing", "", http.StatusOK},
|
||||||
|
{"no trailing slash with slash", "/no-trailing/", "", http.StatusForbidden},
|
||||||
|
{"yes trailing slash exact match", "/yes-trailing/", "", http.StatusOK},
|
||||||
|
{"yes trailing slash without slash", "/yes-trailing", "", http.StatusForbidden},
|
||||||
|
|
||||||
|
{"method-specific hit", "/post-only/", "", http.StatusOK},
|
||||||
|
{"method-specific miss (PUT)", "/put-only/", "", http.StatusForbidden},
|
||||||
|
{"method-specific miss (GET)", "/get-only/", "", http.StatusForbidden},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue