Skip to content

Commit 7760a5b

Browse files
Validate request method
Use the same validation as net/http. Fixes #1803
1 parent a032ec9 commit 7760a5b

File tree

4 files changed

+117
-4
lines changed

4 files changed

+117
-4
lines changed

bytesconv_table.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bytesconv_table_gen.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,101 @@ func main() {
146146
return table
147147
}()
148148

149+
validMethodValueByteTable := [256]byte{
150+
/*
151+
Same as net/http
152+
153+
Method = "OPTIONS" ; Section 9.2
154+
| "GET" ; Section 9.3
155+
| "HEAD" ; Section 9.4
156+
| "POST" ; Section 9.5
157+
| "PUT" ; Section 9.6
158+
| "DELETE" ; Section 9.7
159+
| "TRACE" ; Section 9.8
160+
| "CONNECT" ; Section 9.9
161+
| extension-method
162+
extension-method = token
163+
token = 1*<any CHAR except CTLs or separators>
164+
*/
165+
'!': 1,
166+
'#': 1,
167+
'$': 1,
168+
'%': 1,
169+
'&': 1,
170+
'\'': 1,
171+
'*': 1,
172+
'+': 1,
173+
'-': 1,
174+
'.': 1,
175+
'0': 1,
176+
'1': 1,
177+
'2': 1,
178+
'3': 1,
179+
'4': 1,
180+
'5': 1,
181+
'6': 1,
182+
'7': 1,
183+
'8': 1,
184+
'9': 1,
185+
'A': 1,
186+
'B': 1,
187+
'C': 1,
188+
'D': 1,
189+
'E': 1,
190+
'F': 1,
191+
'G': 1,
192+
'H': 1,
193+
'I': 1,
194+
'J': 1,
195+
'K': 1,
196+
'L': 1,
197+
'M': 1,
198+
'N': 1,
199+
'O': 1,
200+
'P': 1,
201+
'Q': 1,
202+
'R': 1,
203+
'S': 1,
204+
'T': 1,
205+
'U': 1,
206+
'W': 1,
207+
'V': 1,
208+
'X': 1,
209+
'Y': 1,
210+
'Z': 1,
211+
'^': 1,
212+
'_': 1,
213+
'`': 1,
214+
'a': 1,
215+
'b': 1,
216+
'c': 1,
217+
'd': 1,
218+
'e': 1,
219+
'f': 1,
220+
'g': 1,
221+
'h': 1,
222+
'i': 1,
223+
'j': 1,
224+
'k': 1,
225+
'l': 1,
226+
'm': 1,
227+
'n': 1,
228+
'o': 1,
229+
'p': 1,
230+
'q': 1,
231+
'r': 1,
232+
's': 1,
233+
't': 1,
234+
'u': 1,
235+
'v': 1,
236+
'w': 1,
237+
'x': 1,
238+
'y': 1,
239+
'z': 1,
240+
'|': 1,
241+
'~': 1,
242+
}
243+
149244
w := bytes.NewBufferString(pre)
150245
fmt.Fprintf(w, "const hex2intTable = %q\n", hex2intTable)
151246
fmt.Fprintf(w, "const toLowerTable = %q\n", toLowerTable)
@@ -154,6 +249,7 @@ func main() {
154249
fmt.Fprintf(w, "const quotedPathShouldEscapeTable = %q\n", quotedPathShouldEscapeTable)
155250
fmt.Fprintf(w, "const validHeaderFieldByteTable = %q\n", validHeaderFieldByteTable)
156251
fmt.Fprintf(w, "const validHeaderValueByteTable = %q\n", validHeaderValueByteTable)
252+
fmt.Fprintf(w, "const validMethodValueByteTable = %q\n", validMethodValueByteTable)
157253

158254
if err := os.WriteFile("bytesconv_table.go", w.Bytes(), 0o660); err != nil {
159255
log.Fatal(err)

header.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2855,6 +2855,15 @@ func (h *ResponseHeader) parseFirstLine(buf []byte) (int, error) {
28552855
return len(buf) - len(bNext), nil
28562856
}
28572857

2858+
func isValidMethod(method []byte) bool {
2859+
for _, ch := range method {
2860+
if validMethodValueByteTable[ch] == 0 {
2861+
return false
2862+
}
2863+
}
2864+
return true
2865+
}
2866+
28582867
func (h *RequestHeader) parseFirstLine(buf []byte) (int, error) {
28592868
bNext := buf
28602869
var b []byte
@@ -2874,6 +2883,14 @@ func (h *RequestHeader) parseFirstLine(buf []byte) (int, error) {
28742883
return 0, fmt.Errorf("cannot find http request method in %q", buf)
28752884
}
28762885
h.method = append(h.method[:0], b[:n]...)
2886+
2887+
if !isValidMethod(h.method) {
2888+
if h.secureErrorLogMessage {
2889+
return 0, errors.New("unsupported http request method")
2890+
}
2891+
return 0, fmt.Errorf("unsupported http request method %q in %q", h.method, buf)
2892+
}
2893+
28772894
b = b[n+1:]
28782895

28792896
// parse requestURI

header_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2668,10 +2668,6 @@ func TestRequestHeaderReadSuccess(t *testing.T) {
26682668
testRequestHeaderReadSuccess(t, h, "POST /abc HTTP/1.1\r\nHost: aa.com\r\nContent-Type: adv\r\n\r\n123456",
26692669
-2, "/abc", "aa.com", "", "adv")
26702670

2671-
// invalid method
2672-
testRequestHeaderReadSuccess(t, h, "POST /foo/bar HTTP/1.1\r\nHost: google.com\r\n\r\nmnbv",
2673-
-2, "/foo/bar", "google.com", "", "")
2674-
26752671
// put request
26762672
testRequestHeaderReadSuccess(t, h, "PUT /faa HTTP/1.1\r\nHost: aaa.com\r\nContent-Length: 123\r\nContent-Type: aaa\r\n\r\nxwwere",
26772673
123, "/faa", "aaa.com", "", "aaa")
@@ -2767,6 +2763,9 @@ func TestRequestHeaderReadError(t *testing.T) {
27672763

27682764
// Zero-length header
27692765
testRequestHeaderReadError(t, h, "GET /foo/bar HTTP/1.1\r\n: zero-key\r\n\r\n")
2766+
2767+
// Invalid method
2768+
testRequestHeaderReadError(t, h, "G(ET /foo/bar HTTP/1.1\r\n: zero-key\r\n\r\n")
27702769
}
27712770

27722771
func TestRequestHeaderReadSecuredError(t *testing.T) {

0 commit comments

Comments
 (0)