Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for parsing UUIDs between braces #552

Merged
merged 2 commits into from Nov 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions CONTRIBUTING.md
Expand Up @@ -118,6 +118,10 @@ You can follow [this link][lrus] to look for issues like this.

[lrus]: https://github.com/uuid-rs/uuid/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc

# Fuzzing
We use [`cargo fuzz`] to fuzz test various parts of `uuid`. See their guide
for more details on what fuzzing is and how to run the tests yourself.

# Helpful Links
[Helpful Links]: #helpful-links

Expand All @@ -133,3 +137,4 @@ seasoned developers, some useful places to look for information are:
[u-r-l-o]: https://users.rust-lang.org
[Discussions]: https://github.com/uuid-rs/uuid/discussions
[search existing issues]: https://github.com/uuid-rs/uuid/search?q=&type=Issues&utf8=%E2%9C%93
[`cargo fuzz`]: https://rust-fuzz.github.io/book/cargo-fuzz.html
46 changes: 46 additions & 0 deletions examples/windows_guid.rs
Expand Up @@ -49,6 +49,52 @@ fn guid_to_uuid() {
);
}

#[test]
#[cfg(windows)]
fn guid_to_uuid_le_encoded() {
use uuid::Uuid;
use winapi::shared::guiddef;

// A GUID might not be encoded directly as a UUID
// If its fields are stored in little-endian order they might
// need to be flipped. Whether or not this is necessary depends
// on the source of the GUID
let guid_in = guiddef::GUID {
Data1: 0x9d22354a,
Data2: 0x2755,
Data3: 0x304f,
Data4: [0x86, 0x47, 0x9d, 0xc5, 0x4e, 0x1e, 0xe1, 0xe8],
};

let uuid = Uuid::from_fields_le(
guid_in.Data1,
guid_in.Data2,
guid_in.Data3,
&guid_in.Data4,
);

let guid_out = {
let fields = uuid.to_fields_le();

guiddef::GUID {
Data1: fields.0,
Data2: fields.1,
Data3: fields.2,
Data4: *fields.3,
}
};

assert_eq!(
(guid_in.Data1, guid_in.Data2, guid_in.Data3, guid_in.Data4),
(
guid_out.Data1,
guid_out.Data2,
guid_out.Data3,
guid_out.Data4
)
);
}

#[test]
#[cfg(windows)]
fn uuid_from_cocreateguid() {
Expand Down
3 changes: 3 additions & 0 deletions fuzz/.gitignore
@@ -0,0 +1,3 @@
target
corpus
artifacts
25 changes: 25 additions & 0 deletions fuzz/Cargo.toml
@@ -0,0 +1,25 @@
[package]
name = "uuid-fuzz"
version = "0.0.0"
authors = ["Automatically generated"]
publish = false
edition = "2018"

[package.metadata]
cargo-fuzz = true

[dependencies]
libfuzzer-sys = "0.4"

[dependencies.uuid]
path = ".."

# Prevent this from interfering with workspaces
[workspace]
members = ["."]

[[bin]]
name = "fuzz_target_parse"
path = "fuzz_targets/fuzz_target_parse.rs"
test = false
doc = false
1 change: 1 addition & 0 deletions fuzz/corpus/fuzz_target_parse/guid
@@ -0,0 +1 @@
{6d93bade-bd9f-4e13-8914-9474e1e3567b}
1 change: 1 addition & 0 deletions fuzz/corpus/fuzz_target_parse/hyphenated
@@ -0,0 +1 @@
67e55044-10b1-426f-9247-bb680e5fe0c8
1 change: 1 addition & 0 deletions fuzz/corpus/fuzz_target_parse/simple
@@ -0,0 +1 @@
67e5504410b1426f9247bb680e5fe0c8
1 change: 1 addition & 0 deletions fuzz/corpus/fuzz_target_parse/urn
@@ -0,0 +1 @@
urn:uuid:67e55044-10b1-426f-9247-bb680e5fe0c8
12 changes: 12 additions & 0 deletions fuzz/fuzz_targets/fuzz_target_parse.rs
@@ -0,0 +1,12 @@
#![no_main]
use libfuzzer_sys::fuzz_target;

use std::str;
use uuid::Uuid;

fuzz_target!(|data: &[u8]| {
if let Ok(uuid) = str::from_utf8(data) {
// Ensure the parser doesn't panic
let _ = Uuid::parse_str(uuid);
}
});
9 changes: 8 additions & 1 deletion shared/parser.rs
Expand Up @@ -32,9 +32,16 @@ pub fn parse_str(mut input: &str) -> Result<[u8; 16], Error> {
// Ensure length is valid for any of the supported formats
let len = input.len();

// Check for a URN prefixed UUID
if len == 45 && input.starts_with("urn:uuid:") {
input = &input[9..];
} else if !len_matches_any(len, &[36, 32]) {
}
// Check for a Microsoft GUID wrapped in {}
else if len == 38 && input.starts_with("{") && input.ends_with("}") {
input = &input[1..input.len() - 1];
}
// In other cases, check for a simple or hyphenated UUID
else if !len_matches_any(len, &[36, 32]) {
return Err(ErrorKind::InvalidLength {
expected: ExpectedLength::Any(&[36, 32]),
found: len,
Expand Down
30 changes: 29 additions & 1 deletion src/external/serde_support.rs
Expand Up @@ -11,7 +11,10 @@

use crate::{
error::*,
fmt::{Hyphenated, HyphenatedRef, Simple, SimpleRef, Urn, UrnRef},
fmt::{
Braced, BracedRef, Hyphenated, HyphenatedRef, Simple, SimpleRef, Urn,
UrnRef,
},
std::fmt,
Uuid,
};
Expand Down Expand Up @@ -90,6 +93,24 @@ impl Serialize for UrnRef<'_> {
}
}

impl Serialize for Braced {
fn serialize<S: Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.encode_lower(&mut Uuid::encode_buffer()))
}
}

impl Serialize for BracedRef<'_> {
fn serialize<S: Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.encode_lower(&mut Uuid::encode_buffer()))
}
}

impl<'de> Deserialize<'de> for Uuid {
fn deserialize<D: Deserializer<'de>>(
deserializer: D,
Expand Down Expand Up @@ -240,6 +261,13 @@ mod serde_tests {
serde_test::assert_ser_tokens(&u.to_urn(), &[Token::Str(uuid_str)]);
}

#[test]
fn test_serialize_braced() {
let uuid_str = "{f9168c5e-ceb2-4faa-b6bf-329bf39fa1e4}";
let u = Uuid::parse_str(uuid_str).unwrap();
serde_test::assert_ser_tokens(&u.to_braced(), &[Token::Str(uuid_str)]);
}

#[test]
fn test_serialize_compact() {
let uuid_bytes = b"F9168C5E-CEB2-4F";
Expand Down