Review follow-up: keep alias sorting working, validate fetch keys, share the path check - #16329
Merged
jamesfredley merged 1 commit intoSep 9, 2026
Conversation
Fixes the regression and the gaps found in review of the dynamic finder and HQL list sort validation: - Sorting by a criteria or where-query alias regressed: the validator rejected any first path segment that is not a persistent property, which is exactly what an alias is, so WhereQueryWithAssociationSortSpec failed in both Hibernate modules. A first segment that is not a persistent property is now accepted on the shape check alone and left to the query implementation; segments beneath a known property must still resolve through the mapping. - fetch keys in HqlListQueryBuilder were still concatenated into HQL unchecked and are now validated the same way as sort keys. - Composite identities are recognised explicitly instead of relying on the by-name property map, and the identity fallback no longer depends on the segment position. - validateSortProperty is private, the duplicated pattern is replaced by a shared NameUtils.isValidPropertyPath that follows Java identifier rules ($ and non-ASCII letters), and messages no longer echo the untrusted value or distinguish malformed from unknown names. - Blank sort properties are rejected instead of producing an empty order-by part, and sort directions are trimmed before checking. - The BuildableCriteria overload takes each sort map entry's direction from its value with the same asc fallback as applySortForMap, and the entity resolution helper is reused for the identity default. SortArgumentValidationSpec exercises every entry point against real Hibernate mappings: identity and version, inherited, embedded and association paths, composite identities, the mapping default sort, alias roots, map directions, and the rejected sort, order and fetch values. The datamapping-core, NameUtils and mock builder specs cover the remaining branches. The list() reference and finder docs describe the accepted values.
This was referenced Sep 9, 2026
Contributor
There was a problem hiding this comment.
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Pull request overview
Follow-up to fix dynamic-finder/list sorting validation by restoring alias-based sorting, hardening input validation for sort/order/fetch, and sharing a common “identifier-shaped property path” check.
Changes:
- Add
NameUtils.isValidPropertyPathand adopt it for sort/fetch validation to prevent query-string injection via malformed keys. - Adjust
DynamicFinder.validateSortPropertylogic to allow alias-shaped root segments (e.g.,c1.name) while still validating mapped paths beneath known persistent properties. - Validate
fetchkeys inHqlListQueryBuilderbefore concatenating into HQL; update docs and expand test coverage across modules.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| grails-doc/src/en/ref/Domain Classes/list.adoc | Documents validated sort/order/fetch semantics and alias pass-through behavior. |
| grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/reflect/NameUtils.java | Adds shared isValidPropertyPath implementation used by multiple modules. |
| grails-datastore-core/src/test/groovy/org/grails/datastore/mapping/reflect/NameUtilsSpec.groovy | Adds unit tests for valid/invalid property-path shapes (incl. Unicode and $). |
| grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/finders/DynamicFinder.java | Updates sort validation + ordering logic; extracts helpers; restores alias sorting. |
| grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/finders/DynamicFinderCoverageSpec.groovy | Expands coverage around list/dynamic finder/criteria argument validation. |
| grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/query/HqlListQueryBuilder.java | Validates fetch keys and reworks sort validation to use shared path check. |
| grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/query/HqlListQueryBuilderSpec.groovy | Adds tests for trimmed direction, blank keys, $ identifiers, and fetch-key rejection. |
| grails-data-hibernate7/core/src/test/groovy/grails/gorm/tests/SortArgumentValidationSpec.groovy | New integration-style spec exercising validation against real Hibernate mappings. |
| grails-data-hibernate7/docs/src/docs/asciidoc/querying/finders.adoc | Documents accepted sort shapes and alias pass-through. |
| grails-data-hibernate5/docs/src/docs/asciidoc/querying/finders.adoc | Same as above for Hibernate 5 docs. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+62
to
63
| requireMappedProperty(prop, HibernateQueryArgument.FETCH.value()); | ||
| hql.append(" join fetch e.").append(prop); |
| public static void validateSortProperty(PersistentEntity entity, String sort) { | ||
| if (sort == null || !SORT_PROPERTY_PATTERN.matcher(sort).matches()) { | ||
| throw new IllegalArgumentException("Invalid sort property: " + sort); | ||
| private static void validateSortProperty(PersistentEntity entity, String sort) { |
jamesfredley
merged commit Sep 9, 2026
035f919
into
fix/dynamic-finder-sort-validation
1 check passed
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.
Follow-up to the review on #16312, targeting
fix/dynamic-finder-sort-validation.Blocking items
validateSortPropertyrejected any first path segment that is not a persistent property of the root entity, which is what a criteria / where-query alias is.WhereQueryWithAssociationSortSpecfailed ingrails-data-hibernate7-coreandgrails-data-hibernate5-core, andcreateCriteria().list(sort: 'c.name') { createAlias('club', 'c') }failed the same way. A first segment that is not a persistent property is now accepted on the shape check alone and left to the query implementation; segments beneath a known property must still resolve through the mapping.fetchkeys inHqlListQueryBuilder. They are now validated the same way as sort keys before being concatenated into HQL.Smaller points
validateSortPropertyisprivate static; one generic message that does not echo the caller-supplied value; composite identities checked explicitly viagetCompositeIdentity().NameUtils.isValidPropertyPath, shared by both modules and based onCharacter.isJavaIdentifierStart/Part, so$and non-ASCII identifiers are accepted; ignorable control characters are not. The constant is gone from theARGUMENT_*block.resolvePersistentEntityis reused in thesortObject == null && orderParam != nullbranch.order by , e.name), andnormalizeDirectiontrims, soorder: ' desc'keeps working.applySortForMap), with the sameascfallback for anullvalue in both overloads. This is tested and documented rather than reverted.Tests
SortArgumentValidationSpec(hibernate7-core) runs against real mappings throughlist(), dynamic finders, where queries and criteria:sort: 'id'/'version', a subclass sorted by an inherited property, an embedded path, association paths includingclub.id, composite identity members, themapping { sort }default,createAliasand where-query aliases, direction normalization, map directions, valid join fetches, and the rejected sort / order / fetch values.DynamicFinderCoverageSpeccovers nested association traversal, identity and composite identity, a non-association mid-path, a basic collection with no associated entity, alias-shaped roots, the map-direction change and bothpopulateArgumentsForCriteriaoverloads.HqlListQueryBuilderSpeccovers the blank-key and trimmed-direction edges,$identifiers, and injected / unknown fetch keys.NameUtilsSpeccovers the shared path check.Docs
grails-doclist.adocand the Hibernate 7 / Hibernate 5finders.adocdescribe the acceptedsort,orderandfetchvalues and the alias pass-through.