From 2eb57cd0a44e1f841b35f27179d7506bf5d97462 Mon Sep 17 00:00:00 2001 From: Choudhry Shehryar Date: Sat, 3 Oct 2026 16:23:46 +0500 Subject: [PATCH] fix(header): reject double quote in HEADER_CHARS_H2 PR #716 removed `"` (0x22) from HEADER_CHARS so HeaderName::from_bytes (the HTTP/1.1 path) correctly rejects it per the tchar grammar in RFC 9110 5.6.2. It missed the sibling HEADER_CHARS_H2 table, which is used by HeaderName::from_lowercase and from_static and still accepts the byte. from_lowercase is the path HTTP/2 and HTTP/3 implementations use to build header names directly from on-the-wire bytes during decode (e.g. h2's HPACK decoder calls it with no other charset check), so a remote peer could get a literal '"' embedded in a header name that the HTTP/1.1 path would have rejected outright. Add a regression test and align HEADER_CHARS_H2 with HEADER_CHARS at that index; the two tables are otherwise intentionally identical except for case folding (from_bytes lower-cases ASCII letters, from_lowercase rejects uppercase). --- src/header/name.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/header/name.rs b/src/header/name.rs index 38c8693a..d3e38a21 100644 --- a/src/header/name.rs +++ b/src/header/name.rs @@ -1045,7 +1045,7 @@ const HEADER_CHARS_H2: [u8; 256] = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 0, 0, 0, b'!', b'"', b'#', b'$', b'%', b'&', b'\'', // 3x + 0, 0, 0, b'!', 0, b'#', b'$', b'%', b'&', b'\'', // 3x 0, 0, b'*', b'+', 0, b'-', b'.', 0, b'0', b'1', // 4x b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', 0, 0, // 5x 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x @@ -1919,4 +1919,12 @@ mod tests { HeaderName::from_lowercase(&[0x1; 100]).unwrap_err(); HeaderName::from_lowercase(&[0xFF; 100]).unwrap_err(); } + + #[test] + fn test_from_lowercase_rejects_double_quote() { + // HEADER_CHARS_H2 must reject `"` just like HEADER_CHARS does for + // from_bytes(), per the tchar grammar in RFC 9110 5.6.2 (reused by + // RFC 9113 8.2.1 for HTTP/2 field names). + HeaderName::from_lowercase(b"x-evil\"name").unwrap_err(); + } }