uuidjs/uuid

Validate function not recognising c4dbad75-1786-ed4b-b22d-868eb018e488

icodenode opened this issue · 10 comments

Describe the bug

The stringify function throws an error as it is not able to validate the uuid. Example UUID: c4dbad75-1786-ed4b-b22d-868eb018e488. The UUID is showing as valid on a bunch of online validators

How to reproduce

Call verify function on the above-mentioned UUID

Expected behavior

The UUID should be stringified as it is a valid UUID.

Runtime

  • OS: MacOS
  • Runtime: Node.js
  • Runtime Version: 14.0.0

Additional information

[Any other information or comments that you think will help]

The error is correct. ed4b should start with 1-5 to identify the version of generated uuid.

Closing.

@icodenode I'm happy to take a look at what the other "bunch of online validators" are doing if you'd care to name them, but @TrySound is correct. That's not a valid RFC4122 UUID.

We have unfortunately run into this problem as well with a UUID sent to our system by a partner. It got most of the way through out software stack as valid but then failed to validate when it reached a mobile application using uuid.

The UUID in question is 607cb9f0-e3fa-f45d-5b92-9ba660014f60, which if I understand correctly has a version field of f.

This is valid according to postgresql:

api=# select '607cb9f0-e3fa-f45d-5b92-9ba660014f60'::uuid;
                 uuid                 
--------------------------------------
 607cb9f0-e3fa-f45d-5b92-9ba660014f60
(1 row)

api=# select 'foobar'::uuid;
ERROR:  invalid input syntax for type uuid: "foobar"
LINE 1: select 'foobar'::uuid;

And valid according to some online tools:
https://www.freecodeformat.com/validate-uuid-guid.php

But not valid according to uuid:

> require('uuid').validate('607cb9f0-e3fa-f45d-5b92-9ba660014f60')
false

The RFC written in 2005 states...

The following table lists the currently-defined versions for this UUID variant.

...which I think you could read either way, given it's a historical document.

Would a 'loose mode' for validation be acceptable? E.g.

> require('uuid').validate('607cb9f0-e3fa-f45d-5b92-9ba660014f60', { ignoreVersion: true })
true

@djbeaumont Those tools are not correct. Postgresql probably validates loosely for performance reasons.
I don't think it makes sense to provide flags for potentially invalid results. If you need some loose validator you can always write own regexp. It's really not too much code.
https://github.com/uuidjs/uuid/blob/master/src/regex.js

According to https://tools.ietf.org/html/rfc4122#section-4.1.3, f is not a valid version. I agree with @TrySound, we should not add a loose mode.

In fact, reviewing the regex, it should be updated to correctly validate version too... At this moment, according to spec only valid values are 1 to 5.

We already rewrote our code to use a regex that suits us better, but FWIW this behaviour seems to be unusual among UUID implementations. That makes this a bit of a bear trap for developers, since like us they probably won't know there is a problem until it bites them in production.

Python

ᐅ python
Python 3.8.5 (default, Sep  4 2020, 02:22:02) 
[Clang 10.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from uuid import UUID
>>> UUID('foobar')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/anaconda3/lib/python3.8/uuid.py", line 169, in __init__
    raise ValueError('badly formed hexadecimal UUID string')
ValueError: badly formed hexadecimal UUID string
>>> UUID('607cb9f0-e3fa-f45d-5b92-9ba660014f60')
UUID('607cb9f0-e3fa-f45d-5b92-9ba660014f60')

Java

ᐅ jshell
|  Welcome to JShell -- Version 11.0.10
|  For an introduction type: /help intro

jshell> UUID.fromString("foo");
|  Exception java.lang.IllegalArgumentException: Invalid UUID string: foo
|        at UUID.fromString (UUID.java:215)
|        at (#1:1)

jshell> UUID.fromString("607cb9f0-e3fa-f45d-5b92-9ba660014f60");
$2 ==> 607cb9f0-e3fa-f45d-5b92-9ba660014f60

Ruby

2.6.3 :003 > UUID.validate('607cb9f0-e3fa-f45d-5b92-9ba660014f60')
 => true 
2.6.3 :004 > UUID.validate('foobar')
 => nil 

The version field is correct, so UUIDs validate, but they are not currently valid versions, so they shouldn't validate. We don't know if in the future there would be a f version, so...

@piranna wrote:

reviewing the regex, it should be updated to correctly validate version too

I believe regex is correct as it stands. §4.1.3 describes versions 1-5. §4.1.7 describes the special case for nil uuid (effectively version 0). Or were you implying something else?

@djbeaumont wrote:

That makes this a bit of a bear trap for developers, since like us they probably won't know there is a problem until it bites them in production.

I understand how a "loose mode" would make life easier in your case, but I see it as detrimental to the broader community as a whole. "Loose" UUIDs are not well-defined and require more complex (read, "more bug-prone") code to deal with.

You're going to run into "bear traps" anywhere one library's validate function conflicts with what another library expects. So how many permutations of that are there? What does PostGres allow that Python doesn't? Or Go or Rust or Erlang or... or ... or...?

With uuid, you don't have this issue. Won't have this issue. Every UUID it generates or touches is known to be valid per the one definitive source: The RFC.

This is a Good Thing™.

I believe regex is correct as it stands. §4.1.3 describes versions 1-5. §4.1.7 describes the special case for nil uuid (effectively version 0). Or were you implying something else?

Yes, that versions 6 to 15 (6-f) are currently not valid ones, so maybe they should not validate.