http3: reject duplicate pseudo headers (#4993)

This commit is contained in:
pittgi
2025-03-16 03:52:25 +01:00
committed by GitHub
parent 8f23c8a404
commit 3311514d67
2 changed files with 32 additions and 1 deletions

View File

@@ -56,23 +56,33 @@ func parseHeaders(headers []qpack.HeaderField, isRequest bool) (header, error) {
return header{}, fmt.Errorf("received pseudo header %s after a regular header field", h.Name) return header{}, fmt.Errorf("received pseudo header %s after a regular header field", h.Name)
} }
var isResponsePseudoHeader bool // pseudo headers are either valid for requests or for responses var isResponsePseudoHeader bool // pseudo headers are either valid for requests or for responses
var isDuplicatePseudoHeader bool // pseudo headers are allowed to appear exactly once
switch h.Name { switch h.Name {
case ":path": case ":path":
isDuplicatePseudoHeader = hdr.Path != ""
hdr.Path = h.Value hdr.Path = h.Value
case ":method": case ":method":
isDuplicatePseudoHeader = hdr.Method != ""
hdr.Method = h.Value hdr.Method = h.Value
case ":authority": case ":authority":
isDuplicatePseudoHeader = hdr.Authority != ""
hdr.Authority = h.Value hdr.Authority = h.Value
case ":protocol": case ":protocol":
isDuplicatePseudoHeader = hdr.Protocol != ""
hdr.Protocol = h.Value hdr.Protocol = h.Value
case ":scheme": case ":scheme":
isDuplicatePseudoHeader = hdr.Scheme != ""
hdr.Scheme = h.Value hdr.Scheme = h.Value
case ":status": case ":status":
isDuplicatePseudoHeader = hdr.Status != ""
hdr.Status = h.Value hdr.Status = h.Value
isResponsePseudoHeader = true isResponsePseudoHeader = true
default: default:
return header{}, fmt.Errorf("unknown pseudo header: %s", h.Name) return header{}, fmt.Errorf("unknown pseudo header: %s", h.Name)
} }
if isDuplicatePseudoHeader {
return header{}, fmt.Errorf("duplicate pseudo header: %s", h.Name)
}
if isRequest && isResponsePseudoHeader { if isRequest && isResponsePseudoHeader {
return header{}, fmt.Errorf("invalid request pseudo header: %s", h.Name) return header{}, fmt.Errorf("invalid request pseudo header: %s", h.Name)
} }

View File

@@ -235,6 +235,18 @@ var _ = Describe("Request", func() {
Expect(err).To(MatchError(":protocol must be empty")) Expect(err).To(MatchError(":protocol must be empty"))
}) })
It("errors with duplicate pseudo header in request", func() {
headers := []qpack.HeaderField{
{Name: ":path", Value: "/foo"},
{Name: ":authority", Value: "quic.clemente.io"},
{Name: ":method", Value: "GET"},
{Name: ":scheme", Value: "https"},
{Name: ":method", Value: "POST"},
}
_, err := requestFromHeaders(headers)
Expect(err).To(MatchError("duplicate pseudo header: :method"))
})
Context("regular HTTP CONNECT", func() { Context("regular HTTP CONNECT", func() {
It("handles CONNECT method", func() { It("handles CONNECT method", func() {
headers := []qpack.HeaderField{ headers := []qpack.HeaderField{
@@ -432,4 +444,13 @@ var _ = Describe("Response", func() {
_, err := parseTrailers(headers) _, err := parseTrailers(headers)
Expect(err).To(MatchError("http3: received pseudo header in trailer: :status")) Expect(err).To(MatchError("http3: received pseudo header in trailer: :status"))
}) })
It("errors with duplicate status header in response", func() {
headers := []qpack.HeaderField{
{Name: ":status", Value: "200"},
{Name: ":status", Value: "400"},
}
err := updateResponseFromHeaders(&http.Response{}, headers)
Expect(err).To(MatchError("duplicate pseudo header: :status"))
})
}) })