Created
November 6, 2025 09:41
-
-
Save jedisct1/8ea5328ff856a5350432d21f69b78153 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/lib/std/base64.zig b/lib/std/base64.zig | |
| index 8c08fd6786..5eb9a8d283 100644 | |
| --- a/lib/std/base64.zig | |
| +++ b/lib/std/base64.zig | |
| @@ -313,12 +313,22 @@ pub const Base64DecoderWithIgnore = struct { | |
| return result; | |
| } | |
| - /// Return the maximum possible decoded size for a given input length - The actual length may be less if the input includes padding. | |
| - /// `InvalidPadding` is returned if the input length is not valid. | |
| - pub fn calcSizeUpperBound(decoder_with_ignore: *const Base64DecoderWithIgnore, source_len: usize) Error!usize { | |
| - var result = source_len / 4 * 3; | |
| - if (decoder_with_ignore.decoder.pad_char == null) { | |
| - const leftover = source_len % 4; | |
| + /// Return the maximum possible decoded size for a given input - The actual length may be less if the input includes padding. | |
| + /// `InvalidPadding` is returned if the input length is not valid after filtering ignored characters. | |
| + pub fn calcSizeUpperBound(decoder_with_ignore: *const Base64DecoderWithIgnore, source: []const u8) Error!usize { | |
| + var actual_len: usize = 0; | |
| + for (source) |c| { | |
| + if (!decoder_with_ignore.char_is_ignored[c]) { | |
| + actual_len += 1; | |
| + } | |
| + } | |
| + | |
| + var result = actual_len / 4 * 3; | |
| + const leftover = actual_len % 4; | |
| + if (decoder_with_ignore.decoder.pad_char != null) { | |
| + if (leftover != 0) return error.InvalidPadding; | |
| + } else { | |
| + if (leftover == 1) return error.InvalidPadding; | |
| result += leftover * 3 / 4; | |
| } | |
| return result; | |
| @@ -521,7 +531,7 @@ fn testAllApis(codecs: Codecs, expected_decoded: []const u8, expected_encoded: [ | |
| { | |
| const decoder_ignore_nothing = codecs.decoderWithIgnore(""); | |
| var buffer: [0x100]u8 = undefined; | |
| - const decoded = buffer[0..try decoder_ignore_nothing.calcSizeUpperBound(expected_encoded.len)]; | |
| + const decoded = buffer[0..try decoder_ignore_nothing.calcSizeUpperBound(expected_encoded)]; | |
| const written = try decoder_ignore_nothing.decode(decoded, expected_encoded); | |
| try testing.expect(written <= decoded.len); | |
| try testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]); | |
| @@ -531,7 +541,7 @@ fn testAllApis(codecs: Codecs, expected_decoded: []const u8, expected_encoded: [ | |
| fn testDecodeIgnoreSpace(codecs: Codecs, expected_decoded: []const u8, encoded: []const u8) !void { | |
| const decoder_ignore_space = codecs.decoderWithIgnore(" "); | |
| var buffer: [0x100]u8 = undefined; | |
| - const decoded = buffer[0..try decoder_ignore_space.calcSizeUpperBound(encoded.len)]; | |
| + const decoded = buffer[0..try decoder_ignore_space.calcSizeUpperBound(encoded)]; | |
| const written = try decoder_ignore_space.decode(decoded, encoded); | |
| try testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]); | |
| } | |
| @@ -568,3 +578,37 @@ fn testFourBytesDestNoSpaceLeftError(codecs: Codecs, encoded: []const u8) !void | |
| return error.ExpectedError; | |
| } else |err| if (err != error.NoSpaceLeft) return err; | |
| } | |
| + | |
| +test "base64 calcSizeUpperBound with ignored chars validates padding" { | |
| + { | |
| + const decoder = standard.decoderWithIgnore(" "); | |
| + const input = "YWJj ZGVm Z2g="; // "abcdefgh" with spaces | |
| + const size = try decoder.calcSizeUpperBound(input); | |
| + try testing.expect(size > 0); | |
| + } | |
| + | |
| + { | |
| + const decoder = standard.decoderWithIgnore(" "); | |
| + const invalid_input = "YWJj ZGV mZ"; // 9 chars with spaces -> 7 base64 chars, invalid for padded | |
| + const result = decoder.calcSizeUpperBound(invalid_input); | |
| + try testing.expectError(error.InvalidPadding, result); | |
| + } | |
| + | |
| + { | |
| + const decoder = url_safe_no_pad.decoderWithIgnore(" "); | |
| + // 10 chars after filtering (valid: not 1 mod 4) | |
| + const input1 = "YWJj ZGVm Z2g"; // 10 base64 chars | |
| + _ = try decoder.calcSizeUpperBound(input1); | |
| + | |
| + // 9 chars after filtering (valid: not 1 mod 4) | |
| + const input2 = "YWJj ZGVm Zw"; // 9 base64 chars | |
| + _ = try decoder.calcSizeUpperBound(input2); | |
| + } | |
| + | |
| + { | |
| + const decoder = url_safe_no_pad.decoderWithIgnore(" "); | |
| + const input = "YWJj Z"; // 5 chars -> length 1 mod 4, always invalid | |
| + const result = decoder.calcSizeUpperBound(input); | |
| + try testing.expectError(error.InvalidPadding, result); | |
| + } | |
| +} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment