fix(server): reject NUL characters in nest query search

SQLite LIKE stops matching at an embedded NUL, so search=Alpha%00x
behaved like 'Alpha' followed by a wildcard and returned rows that do
not contain the whole search text. The shared query validation now
rejects NUL, so the server answers 400 and the client throws before
sending.
This commit is contained in:
aj committed 2026-10-02 20:16:00 -04:00
1 parent 96267e760e
commit eace08f351
4 files changed
+8 -1

No files matched your search

+3
View File
@@ -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;
}
}
+2
View File
@@ -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);
+1
View File
@@ -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]
+2 -1
View File
@@ -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.