Skip to content

Commit 1cafdfb

Browse files
aclementsgopherbot
authored andcommitted
net/http: make the zero value of CrossOriginProtection work
Currently, CrossOriginProtection must be constructed by NewCrossOriginProtection. If you try to use the zero value, most methods will panic with a nil dereference. This CL makes CrossOriginProtection use on-demand initialization instead, so the zero value has the same semantics as the value currently returned by NewCrossOriginProtection. Now, NewCrossOriginProtection just constructs the zero value. We keep NewCrossOriginProtection by analogy to NewServeMux. Updates #73626 Fixes #74089. Change-Id: Ia80183eb6bfdafb0e002271c0b25c2d6230a159a Reviewed-on: https://go-review.googlesource.com/c/go/+/680396 Auto-Submit: Austin Clements <austin@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Damien Neil <dneil@google.com>
1 parent a35701b commit 1cafdfb

File tree

1 file changed

+28
-9
lines changed

1 file changed

+28
-9
lines changed

src/net/http/csrf.go

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,23 @@ import (
2626
// Requests without Sec-Fetch-Site or Origin headers are currently assumed to be
2727
// either same-origin or non-browser requests, and are allowed.
2828
//
29+
// The zero value of CrossOriginProtection is valid and has no trusted origins
30+
// or bypass patterns.
31+
//
2932
// [Sec-Fetch-Site]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Site
3033
// [Origin]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin
3134
// [Cross-Site Request Forgery (CSRF)]: https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/CSRF
3235
// [safe methods]: https://developer.mozilla.org/en-US/docs/Glossary/Safe/HTTP
3336
type CrossOriginProtection struct {
34-
bypass *ServeMux
37+
bypass atomic.Pointer[ServeMux]
3538
trustedMu sync.RWMutex
3639
trusted map[string]bool
3740
deny atomic.Pointer[Handler]
3841
}
3942

4043
// NewCrossOriginProtection returns a new [CrossOriginProtection] value.
4144
func NewCrossOriginProtection() *CrossOriginProtection {
42-
return &CrossOriginProtection{
43-
bypass: NewServeMux(),
44-
trusted: make(map[string]bool),
45-
}
45+
return &CrossOriginProtection{}
4646
}
4747

4848
// AddTrustedOrigin allows all requests with an [Origin] header
@@ -70,6 +70,9 @@ func (c *CrossOriginProtection) AddTrustedOrigin(origin string) error {
7070
}
7171
c.trustedMu.Lock()
7272
defer c.trustedMu.Unlock()
73+
if c.trusted == nil {
74+
c.trusted = make(map[string]bool)
75+
}
7376
c.trusted[origin] = true
7477
return nil
7578
}
@@ -82,7 +85,21 @@ var noopHandler = HandlerFunc(func(w ResponseWriter, r *Request) {})
8285
// AddInsecureBypassPattern can be called concurrently with other methods
8386
// or request handling, and applies to future requests.
8487
func (c *CrossOriginProtection) AddInsecureBypassPattern(pattern string) {
85-
c.bypass.Handle(pattern, noopHandler)
88+
var bypass *ServeMux
89+
90+
// Lazily initialize c.bypass
91+
for {
92+
bypass = c.bypass.Load()
93+
if bypass != nil {
94+
break
95+
}
96+
bypass = NewServeMux()
97+
if c.bypass.CompareAndSwap(nil, bypass) {
98+
break
99+
}
100+
}
101+
102+
bypass.Handle(pattern, noopHandler)
86103
}
87104

88105
// SetDenyHandler sets a handler to invoke when a request is rejected.
@@ -149,9 +166,11 @@ func (c *CrossOriginProtection) Check(req *Request) error {
149166
// isRequestExempt checks the bypasses which require taking a lock, and should
150167
// be deferred until the last moment.
151168
func (c *CrossOriginProtection) isRequestExempt(req *Request) bool {
152-
if _, pattern := c.bypass.Handler(req); pattern != "" {
153-
// The request matches a bypass pattern.
154-
return true
169+
if bypass := c.bypass.Load(); bypass != nil {
170+
if _, pattern := bypass.Handler(req); pattern != "" {
171+
// The request matches a bypass pattern.
172+
return true
173+
}
155174
}
156175

157176
c.trustedMu.RLock()

0 commit comments

Comments
 (0)