Skip to content

Format-specific UUID parsers can report incorrect error diagnostics #880

@trrk

Description

@trrk

When parsing UUIDs through format-specific types, some invalid inputs produce incorrect error diagnostics.

Reproduction

  • uuid: 1.23.0
use std::str::FromStr;
use uuid::fmt::{Hyphenated, Simple, Urn, Braced};

fn main() {
    // Misleading and internally inconsistent error: "expected length 32 ... found 32".
    // This is parsed as Hyphenated, but it is reported as a Simple-style length error.
    println!("{}", Hyphenated::from_str("550e8400e29b41d4a716446655440000").unwrap_err());

    // Self-contradictory and misclassified error: "expected 12, found 12".
    // Simple has no groups at all.
    println!("{}", Simple::from_str("550e8400-e29b-41d4-a716-446655440000").unwrap_err());

    // Self-contradictory and misclassified error: "expected 12, found 12".
    // The actual problem, the missing `urn:uuid:` prefix, is never mentioned.
    println!("{}", Urn::from_str("550e8400-e29b-41d4-a716-446655440000").unwrap_err());

    // Self-contradictory and misclassified error: "expected 12, found 12".
    // The actual problem, the missing braces, is never mentioned.
    println!("{}", Braced::from_str("550e8400-e29b-41d4-a716-446655440000").unwrap_err());
}

Output:

invalid length: expected length 32 for simple format, found 32
invalid group length in group 4: expected 12, found 12
invalid group length in group 4: expected 12, found 12
invalid group length in group 4: expected 12, found 12

Expected behavior

The parsers should still reject these inputs. However, a format-specific parser should not report an error as if a different UUID format had been requested. The diagnostic should also not report equal expected and actual values, such as expected 12, found 12.

Suspected cause

uuid/src/fmt.rs

Lines 871 to 909 in ca0c85f

impl FromStr for Hyphenated {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
crate::parser::parse_hyphenated(s.as_bytes())
.map(|b| Hyphenated(Uuid(b)))
.map_err(|invalid| invalid.into_err())
}
}
impl FromStr for Simple {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
crate::parser::parse_simple(s.as_bytes())
.map(|b| Simple(Uuid(b)))
.map_err(|invalid| invalid.into_err())
}
}
impl FromStr for Urn {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
crate::parser::parse_urn(s.as_bytes())
.map(|b| Urn(Uuid(b)))
.map_err(|invalid| invalid.into_err())
}
}
impl FromStr for Braced {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
crate::parser::parse_braced(s.as_bytes())
.map(|b| Braced(Uuid(b)))
.map_err(|invalid| invalid.into_err())
}
}

The format-specific parsers call their corresponding strict parser first, then convert the lightweight parse failure through InvalidUuid::into_err(). InvalidUuid::into_err() re-analyzes the input string generically in order to construct a detailed Error. As a result, the generated diagnostic may be based on a perspective other than the originally requested format, which can lead to inaccurate error reporting.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions