Repository navigation
Description
Description
string.uuid is documented as requiring "a valid UUID as defined by RFC 4122", but the rule only checks the 8-4-4-4-12 hex shape:
this.matches('^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$')
RFC 4122, and RFC 9562 which obsoletes it, give meaning to two positions in that string:
- the version, the first character of the third group, is
1to8(RFC 9562 section 4.2) - the variant, the first character of the fourth group, is
10xx, i.e.8,9,aorb(section 4.1)
The Nil UUID (section 5.9) and the Max UUID (section 5.10) are the only valid values that follow neither rule. Because neither position is checked, strings with an undefined version or a non-RFC variant are reported as valid UUIDs. string.tuuid (^[0-9a-fA-F]{32}$) has the same gap.
Steps to Reproduce
syntax = "proto3";
package repro;
import "buf/validate/validate.proto";
message Request {
string id = 1 [(buf.validate.field).string.uuid = true];
}import { create } from '@bufbuild/protobuf';
import { createValidator } from '@bufbuild/protovalidate';
import { RequestSchema } from './gen/repro_pb.js';
const validator = createValidator();
for (const id of [
'919108f7-52d1-4320-9bac-f847db4148a8', // v4
'919108f7-52d1-0320-9bac-f847db4148a8', // version 0
'919108f7-52d1-f320-9bac-f847db4148a8', // version f
'919108f7-52d1-4320-cbac-f847db4148a8', // variant c
'919108f7-52d1-4320-0bac-f847db4148a8', // variant 0
]) {
console.log(id, validator.validate(RequestSchema, create(RequestSchema, { id })).kind);
}Expected Behavior
Only the v4 UUID is valid. The other four strings produce a string.uuid violation.
Actual Behavior
All five are valid:
919108f7-52d1-4320-9bac-f847db4148a8 valid
919108f7-52d1-0320-9bac-f847db4148a8 valid
919108f7-52d1-f320-9bac-f847db4148a8 valid
919108f7-52d1-4320-cbac-f847db4148a8 valid
919108f7-52d1-4320-0bac-f847db4148a8 valid
The rule is a CEL expression in validate.proto, so this is not specific to protovalidate-es. Every runtime evaluates the same expression.
Environment
- Operating System: Windows 11
- Version: 10.0.26200
- Compiler/Toolchain: Node.js 24.19.0
- Protobuf Compiler & Version: buf (npm
@bufbuild/buf),protoc-gen-es - Protovalidate Version:
buf.build/bufbuild/protovalidatemain(commit511051f7f4374c3ca873b53ae68a9288),@bufbuild/protovalidate1.2.0
Possible Solution
Check the two positions in both rules, and keep Nil and Max valid. In RE2 syntax:
string.uuid:
^(?:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|0{8}-0{4}-0{4}-0{4}-0{12}|[fF]{8}-[fF]{4}-[fF]{4}-[fF]{4}-[fF]{12})$
string.tuuid:
^(?:[0-9a-fA-F]{12}[1-8][0-9a-fA-F]{3}[89abAB][0-9a-fA-F]{15}|0{32}|[fF]{32})$
The conformance cases today only cover v1 to v5 and Nil as valid, so nothing pins the current behavior. New cases would cover v6, v7, v8 and Max as valid, and an out-of-range version and variant as invalid.
This makes the rules stricter, so values that pass today would start failing. If you would rather keep uuid as a shape check, the alternative is to change the documentation so it no longer says "as defined by RFC 4122".
Which of these two directions would you prefer? I'm happy to open a PR for either one, including the conformance cases.
Additional Context
For comparison, uuid (validate), validator (isUUID) and zod (z.uuid()) check both positions and accept Nil and Max.
Activity
rodaine commentedon Sep 15, 2026
Hey thanks for the report, @otnc! We'll likely want to keep the current rule as-is so that if/when a new version is introduced, we don't erroneously reject this new format. The documentation language should be updated to be clear it only enforces the shape of the types, not that it's indeed a fully-validated UUID.
That said, a
uuid_typesor similar constraint could be added that takes a list of valid versions/variants that can be more strict:Just because of the complexity of the check, we'd likely push this down into a custom CEL function.
otnc commentedon Sep 15, 2026
Thank you for the reply and explanation!
That makes sense. I agree with keeping the current rule to support future UUID versions easily.
Can I work on adding the
uuid_types constraint and submit a PR for it? Let me know what you think about this idea!rodaine commentedon Sep 15, 2026
Please do! We'll want a rule for both
uuid_typesandtuuid_types. Thanks for your help!otnc commentedon Sep 15, 2026
It's the middle of the night in my local time, so I'll get to it after I wake up!
otnc commentedon Sep 16, 2026
Hi @rodaine . I made #532
Please review when you have time!
Clarify that string.uuid and string.tuuid check the shape only (#533)
timostamm commentedon Sep 23, 2026
Docs are fixed to clarify in #533.
#534 makes the same clarification for
(buf.validate.field).bytes.uuid, which should also be considered. Please feel free to open a feature request.