diff --git a/OpenNest.Data/NestQuery.cs b/OpenNest.Data/NestQuery.cs index 40f207a..8916643 100644 --- a/OpenNest.Data/NestQuery.cs +++ b/OpenNest.Data/NestQuery.cs @@ -42,6 +42,9 @@ public sealed class NestQuery return $"limit must be between 1 and {MaxLimit}."; if (NormalizedSearch.Length > MaxSearchLength) return $"search must be at most {MaxSearchLength} characters."; + // SQLite LIKE stops at an embedded NUL, which would turn the rest of the text into a wildcard. + if (NormalizedSearch.Contains('\0')) + return "search must not contain NUL characters."; return null; } } diff --git a/OpenNest.Server.Tests/NestQueryTests.cs b/OpenNest.Server.Tests/NestQueryTests.cs index 9c4e7e5..4c424eb 100644 --- a/OpenNest.Server.Tests/NestQueryTests.cs +++ b/OpenNest.Server.Tests/NestQueryTests.cs @@ -201,6 +201,8 @@ public sealed class NestQueryTests : IDisposable [InlineData("limit=1&limit=2")] [InlineData("serach=beta")] [InlineData("status=quote")] + [InlineData("search=Alpha%00not-present")] + [InlineData("search=%00")] public async Task Query_InvalidParameters_Return400WithoutItems(string queryString) { Seed(3); diff --git a/OpenNest.Tests/Data/NestStorageTests.cs b/OpenNest.Tests/Data/NestStorageTests.cs index a3b44a3..1a41586 100644 --- a/OpenNest.Tests/Data/NestStorageTests.cs +++ b/OpenNest.Tests/Data/NestStorageTests.cs @@ -247,6 +247,7 @@ public class RemoteNestRepositoryTests new NestQuery { Limit = NestQuery.MaxLimit + 1 }, new NestQuery { Search = new string('x', NestQuery.MaxSearchLength + 1) }, new NestQuery { Sort = (NestSortField)99 }, + new NestQuery { Search = "Alpha\0not-present" }, }; [Fact] diff --git a/docs/nest-storage.md b/docs/nest-storage.md index ece0875..8ee1917 100644 --- a/docs/nest-storage.md +++ b/docs/nest-storage.md @@ -67,7 +67,8 @@ outside the shop network without adding one. `GET /api/nests/query` filters, orders and pages in SQLite, so a client receives only the requested page of metadata (never archive bytes): -- `search` (optional): trimmed, at most 200 characters; blank means no filter. +- `search` (optional): trimmed, at most 200 characters, no NUL characters; + blank means no filter. Case-insensitive substring of the whole text in `name`, `customer`, `material`, `madeBy`, `comments` or the status (`ToBeCut` or the display name `To Be Cut`). `%`, `_` and `\` are literal. Dates and numbers are not matched as text.