spring-projects / spring-projects/spring-data-rest

Querydsl filtering silently ignores nested property paths since GH-2572

Open
#2,579 0 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
Dominant language
Java
Stars
958
Forks
568
PR merge metrics
No merged PRs in 30d

Description

spring-data-rest 5.0.6 (and the sibling GH-2572 backports on the other maintenance lines) drops every
nested query parameter, e.g. ?job.id=1, ?address.city=Paris.

The predicate ends up empty and the endpoint silently returns all rows instead of the filtered set.
Nested paths worked in 5.0.5 and are still fully supported by
QuerydslPredicateBuilder (bindings.getPropertyPath resolves dotted paths).

Cause:
QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver#filterByJacksonVisibility
looks up the entire request-parameter key in MappedJacksonProperties#fieldNameToProperty, which is
keyed by TOP-LEVEL Jackson field names only:

PersistentProperty<?> property = properties.getPersistentProperty(entry.getKey()); // "job.id"
if (property != null) { filtered.put(property.getName(), entry.getValue()); }

"job.id" is never a key, so the parameter is discarded regardless of visibility.
This affects associations that are fully exposed:

in our case the property has no @JsonIgnore and no @RestResource(exported = false),
and getPersistentProperty("job") resolves fine, while getPersistentProperty("job.id") returns null.

The two tests added in 3dc76b88 cover single-segment keys only (@JsonIgnore and @JsonProperty renames),
which is presumably why the regression went unnoticed.

Impact: silent — no 400, no warning. A multi-tenant reporting endpoint filtered by ?job.id=
returns the tenant's entire history instead of one job's rows.

Suggested fix — resolve the key segment by segment, rejecting it if ANY segment is hidden.
This keeps the GH-2572 guarantee (and extends it to nested levels) while restoring nested paths:

private @Nullable String resolvePath(Class<?> type, String key) {
    StringBuilder resolved = new StringBuilder();
    Class<?> currentType = type;
    for (String segment : DOT.split(key)) {
        MappedJacksonProperties properties = jacksonPropertiesLookup.apply(currentType);
        if (properties == null) return null;
        PersistentProperty<?> property = properties.getPersistentProperty(segment);
        if (property == null) return null;              // hidden at this level -> reject whole key
        if (!resolved.isEmpty()) resolved.append(".");
        resolved.append(property.getName());            // honours @JsonProperty renames per segment
        currentType = property.getActualType();
    }
    return resolved.toString();
}

The resolver already holds jacksonPropertiesLookup as Function<Class<?>, MappedJacksonProperties>, so
no new wiring is needed. Happy to submit a PR with a nested-path test alongside the existing ones.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver#filterByJacksonVisibility and inspect how jacksonPropertiesLookup and MappedJacksonProperties handle request keys. Review the existing single-segment tests covering @JsonIgnore and @JsonProperty renames, then add coverage for nested paths such as job.id and hidden nested segments. Done means visible nested paths are preserved, renamed segments work, and any hidden segment rejects the whole key without restoring the silent unfiltered result.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.