Skip to content

Commit 585f725

Browse files
authored
Validate base64 standard encoding (#1395)
1 parent e07c41b commit 585f725

File tree

2 files changed

+23
-11
lines changed

2 files changed

+23
-11
lines changed

src/validators/config.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use std::borrow::Cow;
22
use std::str::FromStr;
33

4-
use base64::Engine;
4+
use base64::engine::general_purpose::{STANDARD, URL_SAFE};
5+
use base64::{DecodeError, Engine};
56
use pyo3::types::{PyDict, PyString};
67
use pyo3::{intern, prelude::*};
78

@@ -28,14 +29,18 @@ impl ValBytesMode {
2829
pub fn deserialize_string<'py>(self, s: &str) -> Result<EitherBytes<'_, 'py>, ErrorType> {
2930
match self.ser {
3031
BytesMode::Utf8 => Ok(EitherBytes::Cow(Cow::Borrowed(s.as_bytes()))),
31-
BytesMode::Base64 => match base64::engine::general_purpose::URL_SAFE.decode(s) {
32-
Ok(bytes) => Ok(EitherBytes::from(bytes)),
33-
Err(err) => Err(ErrorType::BytesInvalidEncoding {
32+
BytesMode::Base64 => URL_SAFE
33+
.decode(s)
34+
.or_else(|err| match err {
35+
DecodeError::InvalidByte(_, b'/' | b'+') => STANDARD.decode(s),
36+
_ => Err(err),
37+
})
38+
.map(EitherBytes::from)
39+
.map_err(|err| ErrorType::BytesInvalidEncoding {
3440
encoding: "base64".to_string(),
3541
encoding_error: err.to_string(),
3642
context: None,
3743
}),
38-
},
3944
BytesMode::Hex => match hex::decode(s) {
4045
Ok(vec) => Ok(EitherBytes::from(vec)),
4146
Err(err) => Err(ErrorType::BytesInvalidEncoding {

tests/test_json.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -379,19 +379,26 @@ def test_partial_parse():
379379

380380

381381
def test_json_bytes_base64_round_trip():
382-
data = b'hello'
383-
encoded = b'"aGVsbG8="'
384-
assert to_json(data, bytes_mode='base64') == encoded
382+
data = b'\xd8\x07\xc1Tx$\x91F%\xf3\xf3I\xca\xd8@\x0c\xee\xc3\xab\xff\x7f\xd3\xcd\xcd\xf9\xc2\x10\xe4\xa1\xb01e'
383+
encoded_std = b'"2AfBVHgkkUYl8/NJythADO7Dq/9/083N+cIQ5KGwMWU="'
384+
encoded_url = b'"2AfBVHgkkUYl8_NJythADO7Dq_9_083N-cIQ5KGwMWU="'
385+
assert to_json(data, bytes_mode='base64') == encoded_url
385386

386387
v = SchemaValidator({'type': 'bytes'}, {'val_json_bytes': 'base64'})
387-
assert v.validate_json(encoded) == data
388+
assert v.validate_json(encoded_url) == data
389+
assert v.validate_json(encoded_std) == data
390+
391+
with pytest.raises(ValidationError) as exc:
392+
v.validate_json('"wrong!"')
393+
[details] = exc.value.errors()
394+
assert details['type'] == 'bytes_invalid_encoding'
388395

389-
assert to_json({'key': data}, bytes_mode='base64') == b'{"key":"aGVsbG8="}'
396+
assert to_json({'key': data}, bytes_mode='base64') == b'{"key":' + encoded_url + b'}'
390397
v = SchemaValidator(
391398
{'type': 'dict', 'keys_schema': {'type': 'str'}, 'values_schema': {'type': 'bytes'}},
392399
{'val_json_bytes': 'base64'},
393400
)
394-
assert v.validate_json('{"key":"aGVsbG8="}') == {'key': data}
401+
assert v.validate_json(b'{"key":' + encoded_url + b'}') == {'key': data}
395402

396403

397404
def test_json_bytes_base64_invalid():

0 commit comments

Comments
 (0)