authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-05 13:39:17-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-05 13:39:17-07:00
log3055ab7f8639deca318f238f21680776a7149acb
treeda69d3846019d24f3a41b3a77898fd39eb55097e
parent450f3bc9250eca86b470ec29c228165054016c76

std.http.Client: fail header parsing under more conditions

* when HTTP header continuations are used * when content-type or location header occurs more than once

1 files changed, 32 insertions(+), 2 deletions(-)

lib/std/http/Client.zig+32-2
...@@ -96,7 +96,7 @@ pub const Request = struct {...@@ -96,7 +96,7 @@ pub const Request = struct {
96 int64("HTTP/1.1") => .@"HTTP/1.1",96 int64("HTTP/1.1") => .@"HTTP/1.1",
97 else => return error.BadHttpVersion,97 else => return error.BadHttpVersion,
98 };98 };
99 if (first_line[8] != ' ') return error.InvalidHttpHeaders;99 if (first_line[8] != ' ') return error.HttpHeadersInvalid;
100 const status = @intToEnum(http.Status, parseInt3(first_line[9..12].*));100 const status = @intToEnum(http.Status, parseInt3(first_line[9..12].*));
101101
102 var headers: Response.Headers = .{102 var headers: Response.Headers = .{
...@@ -105,12 +105,19 @@ pub const Request = struct {...@@ -105,12 +105,19 @@ pub const Request = struct {
105 };105 };
106106
107 while (it.next()) |line| {107 while (it.next()) |line| {
108 if (line.len == 0) return error.HttpHeadersInvalid;
109 switch (line[0]) {
110 ' ', '\t' => return error.HttpHeaderContinuationsUnsupported,
111 else => {},
112 }
108 var line_it = mem.split(u8, line, ": ");113 var line_it = mem.split(u8, line, ": ");
109 const header_name = line_it.first();114 const header_name = line_it.first();
110 const header_value = line_it.rest();115 const header_value = line_it.rest();
111 if (std.ascii.eqlIgnoreCase(header_name, "location")) {116 if (std.ascii.eqlIgnoreCase(header_name, "location")) {
117 if (headers.location != null) return error.HttpHeadersInvalid;
112 headers.location = header_value;118 headers.location = header_value;
113 } else if (std.ascii.eqlIgnoreCase(header_name, "content-length")) {119 } else if (std.ascii.eqlIgnoreCase(header_name, "content-length")) {
120 if (headers.content_length != null) return error.HttpHeadersInvalid;
114 headers.content_length = try std.fmt.parseInt(u64, header_value, 10);121 headers.content_length = try std.fmt.parseInt(u64, header_value, 10);
115 }122 }
116 }123 }
...@@ -131,6 +138,29 @@ pub const Request = struct {...@@ -131,6 +138,29 @@ pub const Request = struct {
131 return error.TestFailed);138 return error.TestFailed);
132 try testing.expectEqual(@as(?u64, 220), parsed.content_length);139 try testing.expectEqual(@as(?u64, 220), parsed.content_length);
133 }140 }
141
142 test "header continuation" {
143 const example =
144 "HTTP/1.0 200 OK\r\n" ++
145 "Content-Type: text/html;\r\n charset=UTF-8\r\n" ++
146 "Content-Length: 220\r\n\r\n";
147 try testing.expectError(
148 error.HttpHeaderContinuationsUnsupported,
149 Response.Headers.parse(example),
150 );
151 }
152
153 test "extra content length" {
154 const example =
155 "HTTP/1.0 200 OK\r\n" ++
156 "Content-Length: 220\r\n" ++
157 "Content-Type: text/html; charset=UTF-8\r\n" ++
158 "content-length: 220\r\n\r\n";
159 try testing.expectError(
160 error.HttpHeadersInvalid,
161 Response.Headers.parse(example),
162 );
163 }
134 };164 };
135165
136 pub const State = enum {166 pub const State = enum {
...@@ -442,7 +472,7 @@ pub const Request = struct {...@@ -442,7 +472,7 @@ pub const Request = struct {
442 const amt = try req.connection.read(buffer);472 const amt = try req.connection.read(buffer);
443 const data = buffer[0..amt];473 const data = buffer[0..amt];
444 const i = req.response.findHeadersEnd(data);474 const i = req.response.findHeadersEnd(data);
445 if (req.response.state == .invalid) return error.InvalidHttpHeaders;475 if (req.response.state == .invalid) return error.HttpHeadersInvalid;
446476
447 const headers_data = data[0..i];477 const headers_data = data[0..i];
448 if (req.response.header_bytes.items.len + headers_data.len > req.response.max_header_bytes) {478 if (req.response.header_bytes.items.len + headers_data.len > req.response.max_header_bytes) {