You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/docs/querying.md
+16-1Lines changed: 16 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -97,8 +97,23 @@ With this request, the toolkit will:
97
97
98
98
## Limitations
99
99
100
+
JsonApiToolkit enforces the following query limits to ensure predictable performance and security:
101
+
102
+
| Limit | Default | Description |
103
+
|-------|---------|-------------|
104
+
| Max Filters | 50 | Maximum number of filter conditions |
105
+
| Max Filter Groups | 10 | Maximum OR/NOT logical blocks |
106
+
| Max Filter Depth | 3 | Maximum nesting of filter groups |
107
+
| Max Filter Value Length | 1000 | Maximum characters per filter value |
108
+
| Max Include Depth | 3 | Maximum include path depth (e.g., `author.posts.comments`) |
109
+
| Max Page Size | 100 | Maximum items per page (silently clamped) |
110
+
111
+
These limits are configurable via `JsonApiOptions`. See the [Security](security.md#query-complexity-limits) documentation for configuration details.
112
+
113
+
**Additional limitations:**
114
+
100
115
-**Include filter validation**: Filters with dot notation can only be applied to relationships that are explicitly included in the request. Use the `AllowedIncludesAttribute` to control which relationships can be filtered.
101
-
-**Complex nested filtering**: Maximum filter depth is 3 levels (e.g., `entity.relationship.property`).
116
+
-**Filtered includes nesting**: Filtered includes currently support up to 2-level nesting (e.g., `parent.child`). Deeper nesting will fall back to unfiltered includes.
Copy file name to clipboardExpand all lines: docs/docs/security.md
+119-1Lines changed: 119 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -75,6 +75,124 @@ All matching is case-insensitive:
75
75
Invalid patterns are logged as warnings during application startup:
76
76
77
77
```
78
-
AllowedIncludesAttribute validation warnings for UsersController.GetUsers:
78
+
AllowedIncludesAttribute validation warnings for UsersController.GetUsers:
79
79
Pattern 'user.**' contains '**' which is not supported. Use single '*' for wildcards.
80
80
```
81
+
82
+
## Query Complexity Limits
83
+
84
+
JsonApiToolkit enforces configurable limits on query complexity to prevent resource exhaustion attacks.
85
+
86
+
### Configuration
87
+
88
+
Configure limits via `JsonApiOptions` in your `Program.cs`:
89
+
90
+
```csharp
91
+
builder.Services.AddJsonApiToolkit(options=> {
92
+
options.MaxFilters=50; // Max filter conditions (default: 50)
93
+
options.MaxFilterGroups=10; // Max OR/NOT blocks (default: 10)
94
+
options.MaxFilterDepth=3; // Max group nesting depth (default: 3)
95
+
options.MaxFilterValueLength=1000; // Max value string length (default: 1000)
96
+
options.MaxIncludeDepth=3; // Max include path depth (default: 3)
97
+
options.MaxPageSize=100; // Max page size, clamped (default: 100)
98
+
options.DefaultPageSize=10; // Default when not specified (default: 10)
99
+
});
100
+
```
101
+
102
+
### Limit Behaviors
103
+
104
+
| Option | Behavior When Exceeded |
105
+
|--------|----------------------|
106
+
|`MaxFilters`| Returns 400 Bad Request |
107
+
|`MaxFilterGroups`| Returns 400 Bad Request |
108
+
|`MaxFilterDepth`| Returns 400 Bad Request |
109
+
|`MaxFilterValueLength`| Returns 400 Bad Request |
110
+
|`MaxIncludeDepth`| Returns 400 Bad Request |
111
+
|`MaxPageSize`| Silently clamped to max value |
112
+
113
+
### Error Response
114
+
115
+
When limits are exceeded, a 400 Bad Request is returned with details:
116
+
117
+
```json
118
+
{
119
+
"errors": [{
120
+
"status": "400",
121
+
"code": "QUERY_TOO_COMPLEX",
122
+
"title": "Query exceeds complexity limits",
123
+
"detail": "Query contains 75 filters, but maximum allowed is 50. Reduce filter count or configure a higher limit via JsonApiOptions.MaxFilters.",
124
+
"source": { "parameter": "filter" },
125
+
"meta": {
126
+
"limit": 50,
127
+
"actual": 75,
128
+
"configKey": "JsonApiOptions.MaxFilters"
129
+
}
130
+
}]
131
+
}
132
+
```
133
+
134
+
## Filter Path Validation
135
+
136
+
When using `[AllowedIncludes]`, dot-notation filter paths are also validated against the allowed list.
137
+
138
+
### How It Works
139
+
140
+
If you use `filter[author.name]=John` (filtering the primary resource by a related entity's field), the `author` relationship must be in the `AllowedIncludes` list:
141
+
142
+
```csharp
143
+
[HttpGet("posts")]
144
+
[AllowedIncludes("author", "comments")]
145
+
publicasyncTask<IActionResult>GetPosts()
146
+
{
147
+
// filter[author.name]=John ✓ - allowed
148
+
// filter[author.bio]=X ✓ - allowed
149
+
// filter[admin.role]=X ✗ - 403 Forbidden (admin not in AllowedIncludes)
"detail": "Filtering on relationship 'admin' is not allowed. Add 'admin' to AllowedIncludes or remove the filter."
164
+
}]
165
+
}
166
+
```
167
+
168
+
> [!NOTE]
169
+
> This validation only applies when `[AllowedIncludes]` is present. Without the attribute, all filter paths are allowed.
170
+
171
+
## DoS Protection
172
+
173
+
The query complexity limits protect against several denial-of-service attack vectors:
174
+
175
+
### Resource Exhaustion
176
+
Complex queries with many filters or deep nesting can cause excessive CPU usage. Limits on `MaxFilters`, `MaxFilterGroups`, and `MaxFilterDepth` prevent attackers from crafting queries that overwhelm the server.
177
+
178
+
### Stack Overflow
179
+
Deeply nested filter groups could cause stack overflow during recursive expression building. The `MaxFilterDepth` limit and internal recursion guards prevent this.
180
+
181
+
### Memory Exhaustion
182
+
Very large filter values or responses could exhaust server memory. The `MaxFilterValueLength` and `MaxPageSize` limits bound memory usage per request.
183
+
184
+
### Recommended Defaults
185
+
186
+
The default limits are designed to handle typical API usage while blocking abuse:
187
+
188
+
| Limit | Default | Rationale |
189
+
|-------|---------|-----------|
190
+
| MaxFilters | 50 | Covers complex search UIs |
191
+
| MaxFilterGroups | 10 | Allows OR chains for search |
0 commit comments