Skip to content

Commit 4d3df54

Browse files
docs: update documentation for v1.4.0 security features
- Add JsonApiOptions configuration example to getting-started.md - Add Query Complexity Limits section to security.md - Add Filter Path Validation section to security.md - Add DoS Protection section to security.md - Update querying.md Limitations section with enforced limits - Update upgrade-guide.md v1.4.0 section with release details
1 parent 36e2c5a commit 4d3df54

4 files changed

Lines changed: 155 additions & 6 deletions

File tree

docs/docs/getting-started.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,21 @@ dotnet add package Intility.JsonApiToolkit
4141
builder.Services.AddJsonApiToolkit();
4242
```
4343

44+
You can also configure options for query limits and pagination:
45+
46+
```csharp
47+
builder.Services.AddJsonApiToolkit(options => {
48+
options.MaxFilters = 100; // Default: 50
49+
options.MaxFilterGroups = 20; // Default: 10
50+
options.MaxFilterDepth = 5; // Default: 3
51+
options.MaxPageSize = 200; // Default: 100
52+
options.DefaultPageSize = 25; // Default: 10
53+
});
54+
```
55+
56+
> [!TIP]
57+
> See the [Security](security.md#query-complexity-limits) documentation for details on all available options and their security implications.
58+
4459
2. **Inheritance:**
4560
Derive your API controllers from the provided `JsonApiController` to leverage helper methods that return JSON:API compliant responses.
4661

docs/docs/querying.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,23 @@ With this request, the toolkit will:
9797

9898
## Limitations
9999

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+
100115
- **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.
102117

103118
## Attribute Mapping
104119

docs/docs/security.md

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,124 @@ All matching is case-insensitive:
7575
Invalid patterns are logged as warnings during application startup:
7676

7777
```
78-
AllowedIncludesAttribute validation warnings for UsersController.GetUsers:
78+
AllowedIncludesAttribute validation warnings for UsersController.GetUsers:
7979
Pattern 'user.**' contains '**' which is not supported. Use single '*' for wildcards.
8080
```
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+
public async Task<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)
150+
return await JsonApiQueryAsync(_context.Posts, "post");
151+
}
152+
```
153+
154+
### Error Response
155+
156+
When filtering on a non-allowed relationship:
157+
158+
```json
159+
{
160+
"errors": [{
161+
"status": "403",
162+
"title": "Forbidden",
163+
"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 |
192+
| MaxFilterDepth | 3 | Entity → Relationship → Property |
193+
| MaxFilterValueLength | 1000 | Long enough for UUIDs, GUIDs, text search |
194+
| MaxIncludeDepth | 3 | Matches filter depth |
195+
| MaxPageSize | 100 | Prevents large data dumps |
196+
197+
> [!TIP]
198+
> If your application requires higher limits, increase them via configuration. Monitor query performance when raising limits.

docs/docs/upgrade-guide.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
This document tracks all breaking changes, new features, and migration steps for each version of JsonApiToolkit.
44

5-
**Current Version:** 1.2.5
5+
**Current Version:** 1.4.0
66

77
---
88

@@ -227,12 +227,13 @@ public static class JsonApiMapper { ... }
227227

228228
### v1.4.0 - Security Hardening
229229

230-
**Release Date:** TBD
230+
**Release Date:** January 2026
231231

232232
**New Features:**
233-
- [x] `JsonApiOptions` configuration class
234-
- [x] Configurable query complexity limits
233+
- [x] `JsonApiOptions` configuration class for query limits
234+
- [x] Configurable query complexity limits (filters, groups, depth, page size)
235235
- [x] AllowedIncludes now validates filter paths (not just includes)
236+
- [x] Recursion depth guard for nested filter groups
236237

237238
**Configuration:**
238239
```csharp

0 commit comments

Comments
 (0)