Skip to content

Commit

Permalink
Merge pull request #552 from KodrAus/feat/braced-formatting
Browse files Browse the repository at this point in the history
Add support for parsing UUIDs between braces
  • Loading branch information
KodrAus committed Nov 2, 2021
2 parents 7b55e32 + 4b30c32 commit 1414f2d
Show file tree
Hide file tree
Showing 13 changed files with 574 additions and 42 deletions.
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

0 comments on commit 1414f2d

Please sign in to comment.