Reject malformed record queries instead of silently ignoring them - #96
Open
eastagiletracker wants to merge 1 commit into
Open
Conversation
The topic iteration route extracted its query string with Option<Query<TopicIterationQuery>>, so any parameter that failed to parse discarded the whole query string and fell back to defaults. A request carrying a partition filter alongside an unparseable parameter (order=descending, say) returned records from every partition with a 200, giving no sign that the filter had been dropped. PartitionSelector deserialization had a similar fallback: a "regex:" selector that would not compile was treated as an exact partition name, so the query matched nothing and read as an empty topic. Treat a missing query string as an empty one so all-default requests still parse, report serde_qs failures through ErrorReply as the 400 the rest of the API returns, take the query string as a required extractor on the iteration route, and report uncompilable regex selectors when they are deserialized. PartitionSelector::from keeps its infallible fallback, so in-process callers are unaffected.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR proposes rejecting malformed record-query parameters instead of silently ignoring them, so a
partition_filteris never quietly widened to every partition of a topic. We include this PR work along with a full history of your repo at https://eastagiletracker.com/projects/386. You can sign in with your GitHub ID to claim ownership of the project.What goes wrong today
POST /topic/{topic}/recordstakes its query string throughOption<Query<TopicIterationQuery>>. BecauseOption<T>turns an extractor rejection intoNone, a single parameter that fails to parse discards the whole query string and every parameter silently reverts to its default —partition_filterincluded. A caller who scopes a read to one partition and mistypes an unrelated parameter gets records from every partition back, with a 200 and nothing in the response to say the filter was dropped.PartitionSelectorhas a matching fallback of its own: it is deserialized throughFrom<T: AsRef<str>>, which treats aregex:selector that does not compile as an exact partition name. That name matches no partition, so the request reads as an empty topic — again with a 200.Reproduced on
mainat 5c3d741, with 5 records appended to partitionalphaand 5 tobeta, iterating the topic over HTTP:The third row is the filter being dropped:
descendingis not a validTopicIterationOrder, so the filter goes with it and the response carries both partitions. The fifth row is the silent selector fallback — theregexcrate has no look-around, so(?=alpha)becomes the literal partition nameregex:(?=alpha).The change
Querynow treats a missing query string as an empty one, so a request with no parameters still parses to the defaults, and it reports aserde_qsfailure throughErrorReplyas the same 400 with a JSON{code, message}body that the rest of the API returns (it was a bare 406 with an empty body). With that in place the iteration route can takeQuery<TopicIterationQuery>as a required extractor, so a malformed query string is reported rather than discarded.PartitionSelectorgains a fallibleparse, used by a hand-writtenDeserialize, so an uncompilableregex:selector is reported when it arrives over the wire.On backward compatibility: every valid query is unaffected, including requests that send no query string at all, and
PartitionSelector::fromkeeps its infallible fallback so in-process callers and yourpartition_selector_invalid_regextest behave exactly as before. The visible change is that a query string that could not be parsed now returns 400 instead of being ignored (iteration route) or answered with 406 (partition route).ErrorReply::InvalidQueryStringis additive, leaving the existing variants alone.Verification
The four tests added to
server/tests/server.rsdrive the real server over HTTP. Three of them fail onmainbefore the fix and pass after it; the fourth is a control that passes on both, covering an unfiltered request with no query string, an exact-name filter and an equivalentregex:filter:Two unit tests in
transportcoverPartitionSelectordirectly: uncompilable patterns (look-around, an unbalanced group, and one past the compiled size limit) are rejected byparseand byDeserializewhilefromstill falls back, and valid exact andregex:selectors still deserialize and match.make test(cargo test --workspace --features batch,polars,replicate) goes from 168 passing to 174 passing with no failures before or after, andmake check-fmtandmake lintare both clean. One thing worth flagging: #94 editsserver/src/axum_util/query.rsandserver/src/http.rsfor the axum 0.8 move, so this will want a rebase if that lands first. The port is small — under axum 0.8 theOptionalFromRequestPartsimpl that PR adds is what keeps the silent fallback alive, and the iteration route would simply stay on the requiredQueryextractor.How this was managed
This work was tracked as a single story on a board imported from this repository's own issues and pull requests: Reject malformed record-query parameters instead of silently ignoring them, on the board at https://eastagiletracker.com/projects/386.
If you'd rather not receive contributions like this, reply
no-more-prson this pull request and we won't open any further ones on your repositories.Lawrence W. Sinclair
CEO / East Agile
linkedin.com/in/lwsinclair/
eastagile.com