Found during review of #1092.
The client has an identifier bound and does not use it for the offering selector
validate_identifier in crates/registry-scheduling-client/src/client.rs is the
caller-side bound: non-empty, within MAXIMUM_IDENTIFIER_BYTES, and inside the
alphanumeric plus -_. alphabet the runtime's own identifiers live in. The hold and
appointment routes all call it.
The two availability entry points do not. validate_availability checks only
emptiness:
if offering.is_empty() {
return Err(SchedulingClientError::invalid_request(
"the offering selector is invalid",
));
}
and explain repeats that same lone check inline rather than sharing a helper.
Result
The offering travels as a query parameter, so an out-of-grammar value cannot reach a
different route the way a path segment could. What it does instead is waste a round
trip: the client allocates and sends an arbitrarily large query URL for a value it
already has the vocabulary to refuse, and the caller gets back whatever the transport
or an intermediary decides about an oversized URL rather than the caller-side
InvalidRequest the client's other routes promise for the same class of input.
Fix
Call validate_identifier on the offering selector in validate_availability, and
have explain call validate_availability's selector check rather than carrying its
own copy, so both paths refuse before any I/O and there is one definition of a valid
selector.
Found during review of #1092.
The client has an identifier bound and does not use it for the offering selector
validate_identifierincrates/registry-scheduling-client/src/client.rsis thecaller-side bound: non-empty, within
MAXIMUM_IDENTIFIER_BYTES, and inside thealphanumeric plus
-_.alphabet the runtime's own identifiers live in. The hold andappointment routes all call it.
The two availability entry points do not.
validate_availabilitychecks onlyemptiness:
and
explainrepeats that same lone check inline rather than sharing a helper.Result
The offering travels as a query parameter, so an out-of-grammar value cannot reach a
different route the way a path segment could. What it does instead is waste a round
trip: the client allocates and sends an arbitrarily large query URL for a value it
already has the vocabulary to refuse, and the caller gets back whatever the transport
or an intermediary decides about an oversized URL rather than the caller-side
InvalidRequestthe client's other routes promise for the same class of input.Fix
Call
validate_identifieron the offering selector invalidate_availability, andhave
explaincallvalidate_availability's selector check rather than carrying itsown copy, so both paths refuse before any I/O and there is one definition of a valid
selector.