Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions accept.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Copy link
Copy Markdown

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 from good.com succeeds and only other cross origin handshakes get 500 (invalidPatternAfterMatch pins this). The sentence also reads as "every pattern is tried". An operator who tests only from good.com sees no error and ships the invalid entry. Suggested: "Accept tries the patterns in order and stops at the first match."

🤖

// 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.
Expand Down Expand Up @@ -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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P4 [CRF-23] The decision log says "The log.Printf of the pattern error stays." with no reason, unlike the other entry. (Leorio)

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 Accept and sets no ErrorHandler, the log line is the only place an operator sees which entry is invalid. State the actual reason.

🤖

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
}
}
Expand Down
43 changes: 41 additions & 2 deletions accept_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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)

invalidPatternAfterMatch covers {"other.com", "["}, and badOriginPattern uses "[" alone. Changing authenticateOrigin to skip invalid patterns and return the error only when no later pattern matches still passes go test -run 'TestAccept|Test_authenticateOrigin'. Under that change, {"[", "other.com"} accepts https://other.com. Add a row invalidPatternBeforeMatch with origin https://other.com, host example.com, patterns {"[", "other.com"}, and success: false.

🤖

origin: "https://other.com",
host: "example.com",
originPatterns: []string{"other.com", "["},
success: true,
},
}

for _, tc := range testCases {
Expand Down