Repository navigation
fix: answer an invalid OriginPatterns entry with 500 #596
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: mafredri/accept-error-handler
Are you sure you want to change the base?
Changes from all commits
828c5d5
2d49f55
3f3b727
3232878
f2fb7dc
18b66a2
fd4a902
6950739
3b4d4c1
fa62356
5e45c9c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,6 +44,10 @@ type AcceptOptions struct { | |
| // https://golang.org/pkg/path/#Match). By default, it is matched | ||
| // against the request origin host. If the pattern contains a URI | ||
| // scheme ("://"), it will be matched against "scheme://host". | ||
| // Accept tries the patterns in order. When it tries an invalid pattern, | ||
| // it rejects the request with 500 Internal Server Error and an error | ||
| // wrapping path.ErrBadPattern. To find invalid patterns up front, check | ||
| // each with path.Match. | ||
| // | ||
| // Please ensure you understand the ramifications of enabling this. | ||
| // If used incorrectly your WebSocket server will be open to CSRF attacks. | ||
|
|
@@ -126,12 +130,13 @@ func accept(w http.ResponseWriter, r *http.Request, opts *AcceptOptions) (_ *Con | |
| if !opts.InsecureSkipVerify { | ||
| err = authenticateOrigin(r, opts.OriginPatterns) | ||
| if err != nil { | ||
| defaultBody := err.Error() | ||
| status, defaultBody := http.StatusForbidden, err.Error() | ||
| if errors.Is(err, path.ErrBadPattern) { | ||
| log.Printf("websocket: %v", err) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P4 [CRF-23] The decision log says "The A maintainer who reads the squashed commit cannot tell why the log was kept. A likely reason: the default body is only "Internal Server Error", so for a handler that drops the error from
|
||
| defaultBody = http.StatusText(http.StatusForbidden) | ||
| status = http.StatusInternalServerError | ||
| defaultBody = http.StatusText(status) | ||
| } | ||
| opts.writeRejection(w, r, http.StatusForbidden, err, defaultBody) | ||
| opts.writeRejection(w, r, status, err, defaultBody) | ||
| return nil, err | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -207,6 +207,25 @@ func TestAccept(t *testing.T) { | |
| wg.Wait() | ||
| assert.Success(t, err) | ||
| }) | ||
|
|
||
| t.Run("insecureSkipVerifyIgnoresInvalidPattern", func(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| server, client := net.Pipe() | ||
| defer client.Close() | ||
| w := mockHijacker{ | ||
| ResponseWriter: httptest.NewRecorder(), | ||
| hijack: func() (net.Conn, *bufio.ReadWriter, error) { | ||
| return server, bufio.NewReadWriter(bufio.NewReader(server), bufio.NewWriter(server)), nil | ||
| }, | ||
| } | ||
| r := newHandshakeRequest() | ||
| r.Header.Set("Origin", "https://other.com") | ||
|
|
||
| c, err := Accept(w, r, &AcceptOptions{InsecureSkipVerify: true, OriginPatterns: []string{"["}}) | ||
| assert.Success(t, err) | ||
| c.CloseNow() | ||
| }) | ||
| } | ||
|
|
||
| func newHandshakeRequest() *http.Request { | ||
|
|
@@ -320,11 +339,11 @@ var acceptRejectionTests = []struct { | |
| name: "badOriginPattern", | ||
| modify: func(r *http.Request) { r.Header.Set("Origin", "https://other.com") }, | ||
| opts: AcceptOptions{OriginPatterns: []string{"["}}, | ||
| wantStatus: http.StatusForbidden, | ||
| wantStatus: http.StatusInternalServerError, | ||
| wantErr: `invalid OriginPatterns entry "[": syntax error in pattern`, | ||
| wantErrIs: path.ErrBadPattern, | ||
| wantHeader: http.Header{}, | ||
| wantDefaultBody: "Forbidden", | ||
| wantDefaultBody: "Internal Server Error", | ||
| }, | ||
| { | ||
| name: "noHijacker", | ||
|
|
@@ -786,6 +805,26 @@ func Test_authenticateOrigin(t *testing.T) { | |
| }, | ||
| success: true, | ||
| }, | ||
| { | ||
| name: "invalidPatternNoOrigin", | ||
| host: "example.com", | ||
| originPatterns: []string{"["}, | ||
| success: true, | ||
| }, | ||
| { | ||
| name: "invalidPatternSameOrigin", | ||
| origin: "https://example.com", | ||
| host: "example.com", | ||
| originPatterns: []string{"["}, | ||
| success: true, | ||
| }, | ||
| { | ||
| name: "invalidPatternAfterMatch", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P4 [CRF-22] No test puts an invalid pattern before a matching pattern, so the "in order" rule in the new doc is not tested. (Bisky)
|
||
| origin: "https://other.com", | ||
| host: "example.com", | ||
| originPatterns: []string{"other.com", "["}, | ||
| success: true, | ||
| }, | ||
| } | ||
|
|
||
| for _, tc := range testCases { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P4 [CRF-21] The doc says Accept "tries the patterns in order" but not that it stops at the first match, so it does not say which requests never reach an invalid pattern. (Mafuuu)
With
OriginPatterns: []string{"good.com", "["}, every handshake fromgood.comsucceeds and only other cross origin handshakes get 500 (invalidPatternAfterMatchpins this). The sentence also reads as "every pattern is tried". An operator who tests only fromgood.comsees no error and ships the invalid entry. Suggested: "Accept tries the patterns in order and stops at the first match."