authorgravatar for techatrix@mailbox.orgTechatrix <techatrix@mailbox.org> 2026-03-23 00:29:07+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-23 05:22:46+01:00
log029719cf473f6695a8290aa2873624448a47e0c5
tree8dbdd58e9cdbafd4c22f41ae3cc624abb0f801f5
parent1708da2c710bd5fa84167ed4abc7ec79438c9be4

std.Uri: reject invalid URI schemes according to RFC3986


1 files changed, 22 insertions(+), 12 deletions(-)

lib/std/Uri.zig+22-12
...@@ -374,13 +374,11 @@ pub fn fmt(uri: *const Uri, flags: Format.Flags) std.fmt.Alt(Format, Format.defa...@@ -374,13 +374,11 @@ pub fn fmt(uri: *const Uri, flags: Format.Flags) std.fmt.Alt(Format, Format.defa
374/// The return value will contain strings pointing into the original `text`.374/// The return value will contain strings pointing into the original `text`.
375/// Each component that is provided will be non-`null`.375/// Each component that is provided will be non-`null`.
376pub fn parse(text: []const u8) ParseError!Uri {376pub fn parse(text: []const u8) ParseError!Uri {
377 const end = for (text, 0..) |byte, i| {377 const scheme, const rest = std.mem.cutScalar(u8, text, ':') orelse
378 if (!isSchemeChar(byte)) break i;378 return error.InvalidFormat;
379 } else text.len;379 if (!isValidScheme(scheme))
380 // After the scheme, a ':' must appear.380 return error.UnexpectedCharacter;
381 if (end >= text.len) return error.InvalidFormat;381 return parseAfterScheme(scheme, rest);
382 if (text[end] != ':') return error.UnexpectedCharacter;
383 return parseAfterScheme(text[0..end], text[end + 1 ..]);
384}382}
385383
386pub const ResolveInPlaceError = ParseError || error{NoSpaceLeft};384pub const ResolveInPlaceError = ParseError || error{NoSpaceLeft};
...@@ -522,11 +520,16 @@ fn merge_paths(base: Component, new: []u8, aux_buf: *[]u8) error{NoSpaceLeft}!Co...@@ -522,11 +520,16 @@ fn merge_paths(base: Component, new: []u8, aux_buf: *[]u8) error{NoSpaceLeft}!Co
522}520}
523521
524/// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )522/// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
525fn isSchemeChar(c: u8) bool {523fn isValidScheme(scheme: []const u8) bool {
526 return switch (c) {524 if (scheme.len == 0) return false;
527 'A'...'Z', 'a'...'z', '0'...'9', '+', '-', '.' => true,525 if (!std.ascii.isAlphabetic(scheme[0])) return false;
528 else => false,526 for (scheme[1..]) |byte| {
529 };527 switch (byte) {
528 'A'...'Z', 'a'...'z', '0'...'9', '+', '-', '.' => continue,
529 else => return false,
530 }
531 }
532 return true;
530}533}
531534
532/// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"535/// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
...@@ -620,6 +623,13 @@ test "scheme" {...@@ -620,6 +623,13 @@ test "scheme" {
620 try std.testing.expectEqualStrings("ab+", (try parse("ab+:_")).scheme);623 try std.testing.expectEqualStrings("ab+", (try parse("ab+:_")).scheme);
621 try std.testing.expectEqualStrings("X+++", (try parse("X+++:_")).scheme);624 try std.testing.expectEqualStrings("X+++", (try parse("X+++:_")).scheme);
622 try std.testing.expectEqualStrings("Y+-.", (try parse("Y+-.:_")).scheme);625 try std.testing.expectEqualStrings("Y+-.", (try parse("Y+-.:_")).scheme);
626
627 try std.testing.expectError(error.InvalidFormat, parse(""));
628 try std.testing.expectError(error.UnexpectedCharacter, parse(":"));
629 try std.testing.expectError(error.UnexpectedCharacter, parse("-:"));
630 try std.testing.expectError(error.UnexpectedCharacter, parse("+hTTp:"));
631 try std.testing.expectError(error.UnexpectedCharacter, parse("$:"));
632 try std.testing.expectError(error.UnexpectedCharacter, parse("h$:"));
623}633}
624634
625test "authority" {635test "authority" {