Skip to content

fix(filter)!: apply MaxPropertyDepth to property paths in arithmetic - #138

Open
pdevito3 wants to merge 1 commit into
mainfrom
fm/qk-breaking-arithmetic-depth
Open

pdevito3 wants to merge 1 commit into
mainfrom
fm/qk-breaking-arithmetic-depth

Conversation

@pdevito3

@pdevito3 pdevito3 commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

For later consideration in a major version. Do not merge now. #134 removed this check to keep v1.x compatible with v1.14.2 (restore commit 8a09c25). This PR re-applies the depth part of 988fdff (#113). The captain decides on this PR separately.

Summary

-                var leftExpr = temp.leftArithmetic.ToLinqExpression(parameter, typeof(T));
-                var rightExpr = temp.rightSide.ToLinqExpression(parameter, typeof(T));
+                var leftArithmetic = ResolveArithmeticProperties(temp.leftArithmetic, typeof(T), config);
+                var rightArithmetic = ResolveArithmeticProperties(temp.rightSide, typeof(T), config);
+
+                var leftExpr = leftArithmetic.ToLinqExpression(parameter, typeof(T));
+                var rightExpr = rightArithmetic.ToLinqExpression(parameter, typeof(T));

ResolveArithmeticProperties sends each property path in an arithmetic expression through PropertyResolver.Resolve. Resolve applies MaxPropertyDepth and the HasMaxDepth overrides, like for every other property path.

Scope: this PR changes only the depth check. PropertyResolver on main does not map query names, so a query name in arithmetic still throws, as on main. That is item J-bis, in #154. PreventFilter in arithmetic is item I (#136). The rewrite to the member path changes only the letter case, and arithmetic already ignores the case.

v1.14.2 behavior (and main)

A property path inside arithmetic skips the depth check. Every other property path in a filter or a sort obeys MaxPropertyDepth.

New behavior

A property path inside arithmetic obeys MaxPropertyDepth too. A path that is too deep throws QueryKitPropertyDepthExceededException.

Example

var config = new QueryKitConfiguration(c => c.MaxPropertyDepth = 0);
FilterParser.ParseFilter<Ingredient>("(Recipe.Rating + 0) > 1", config);
  • v1.14.2 and main: the filter works and joins Recipe.
  • This PR: the filter throws QueryKitPropertyDepthExceededException. Recipe.Rating == 1 without arithmetic already throws on v1.14.2.

Security risk on v1.14.2

MaxPropertyDepth limits how deep a caller can go into the object graph. With arithmetic, a caller skips the limit: (Recipe.Rating + 0) > 1 passes a limit of 0. A caller can then filter on related data that the limit was set to block, and can make larger joins than the app permits.

Justification

The limit must apply to every property path, or it does not limit anything. Arithmetic was the only path that skipped it.

Migration

If a filter needs a deeper arithmetic path, increase MaxPropertyDepth, or add HasMaxDepth on the property.

README

The Max Property Depth section now tells that the limit also applies to a property path inside an arithmetic expression.

Tests

This test comes back from main before #134, in PropertyResolverTests:

  • arithmetic_property_skips_max_property_depth -> arithmetic_property_obeys_max_property_depth

dotnet test: 470 unit tests and 297 Postgres integration tests (Testcontainers) pass, 0 failures.

Rebase on main

This branch is rebased on current main (#169). The rebase had no conflicts. The breaking change did not change.

Interaction with #151 (item O)

#151 (item O) adds an unknown-property check to the arithmetic Select. If both merge, the second one gets a text conflict. The combined code is the ResolveArithmeticProperties of main before #134, with the depth check and the unknown-property check.

A property path inside an arithmetic expression did not go through the property resolver, so MaxPropertyDepth did not apply to it. A caller could reach deeper navigation properties than the limit permits, for example (Recipe.Rating + 0) > 1 with a limit of 0. Resolve each property in an arithmetic expression, so the depth check applies.

BREAKING CHANGE: a filter with an arithmetic property path deeper than MaxPropertyDepth now throws QueryKitPropertyDepthExceededException.
@pdevito3
pdevito3 force-pushed the fm/qk-breaking-arithmetic-depth branch from aa341d8 to 7cfed06 Compare October 1, 2026 21:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant