Skip to content

Commit 1aa668d

Browse files
committed
fix(routing_trie): better validation on URL queries
1 parent fd188a1 commit 1aa668d

File tree

1 file changed

+25
-13
lines changed

1 file changed

+25
-13
lines changed

src/http/router/routing_trie.zig

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -266,26 +266,29 @@ pub const RoutingTrie = struct {
266266

267267
var duped = try std.ArrayListUnmanaged([]const u8).initCapacity(allocator, 0);
268268
defer duped.deinit(allocator);
269+
errdefer for (duped.items) |d| allocator.free(d);
269270

270271
if (query_pos) |pos| {
271272
if (path.len > pos + 1) {
272273
var query_iter = std.mem.tokenizeScalar(u8, path[pos + 1 ..], '&');
273274

274275
while (query_iter.next()) |chunk| {
275-
const field_idx = std.mem.indexOfScalar(u8, chunk, '=') orelse break;
276-
if (chunk.len < field_idx + 1) break;
276+
const field_idx = std.mem.indexOfScalar(u8, chunk, '=') orelse return error.QueryMissingValue;
277+
if (chunk.len < field_idx + 2) return error.QueryMissingValue;
277278

278279
const key = chunk[0..field_idx];
279280
const value = chunk[(field_idx + 1)..];
280281

281-
assert(std.mem.indexOfScalar(u8, key, '=') == null);
282-
assert(std.mem.indexOfScalar(u8, value, '=') == null);
282+
if (std.mem.indexOfScalar(u8, key, '=') != null) return error.MalformedQueryKey;
283+
if (std.mem.indexOfScalar(u8, value, '=') != null) return error.MalformedQueryValue;
283284

284285
const decoded_key = try decode_alloc(allocator, key);
285286
try duped.append(allocator, decoded_key);
286287

287288
const decoded_value = try decode_alloc(allocator, value);
288289
try duped.append(allocator, decoded_value);
290+
291+
// Later values will clobber earlier ones.
289292
try queries.put(decoded_key, decoded_value);
290293
}
291294
}
@@ -519,17 +522,26 @@ test "Routing with Queries" {
519522
{
520523
q.clearRetainingCapacity();
521524
// Purposefully bad format with incomplete key/value pair.
522-
const captured = (try s.get_bundle(
525+
const captured = s.get_bundle(testing.allocator, "/item/100/price/283.21?help", captures[0..], &q);
526+
try testing.expectError(error.QueryMissingValue, captured);
527+
}
528+
529+
{
530+
q.clearRetainingCapacity();
531+
// Purposefully bad format with incomplete key/value pair.
532+
const captured = s.get_bundle(testing.allocator, "/item/100/price/283.21?help=", captures[0..], &q);
533+
try testing.expectError(error.QueryMissingValue, captured);
534+
}
535+
536+
{
537+
q.clearRetainingCapacity();
538+
// Purposefully bad format with invalid charactes.
539+
const captured = s.get_bundle(
523540
testing.allocator,
524-
"/item/100/price/283.21?help",
541+
"/item/999/price/100.221?page_count=pages=2020&abc=200",
525542
captures[0..],
526543
&q,
527-
)).?;
528-
defer testing.allocator.free(captured.duped);
529-
defer for (captured.duped) |dupe| testing.allocator.free(dupe);
530-
try testing.expectEqual(Route.init("/item/%i/price/%f"), captured.route);
531-
try testing.expectEqual(100, captured.captures[0].signed);
532-
try testing.expectEqual(283.21, captured.captures[1].float);
533-
try testing.expectEqual(0, q.count());
544+
);
545+
try testing.expectError(error.MalformedQueryValue, captured);
534546
}
535547
}

0 commit comments

Comments
 (0)