diff options
| author | grm <grm@eyesin.space> | 2026-09-18 13:50:35 +0300 |
|---|---|---|
| committer | grm <grm@eyesin.space> | 2026-09-18 13:50:42 +0300 |
| commit | c3026c34b042cc044cddfc5674d5f5ad69bb845d (patch) | |
| tree | 7111e8fc310b193c510110e813befb00695a7597 /internal/web/search.go | |
| parent | c47397ac1e2ceafafe2be3cdec86366dd396ed6f (diff) | |
| download | blogspace-c3026c34b042cc044cddfc5674d5f5ad69bb845d.tar.gz blogspace-c3026c34b042cc044cddfc5674d5f5ad69bb845d.tar.bz2 blogspace-c3026c34b042cc044cddfc5674d5f5ad69bb845d.zip | |
Security: Throttle search, cap its words and give the query a deadline
/search runs an unindexed regular-expression scan over every published
post, built from up to fifty ".*"-joined words, for anyone who asks —
the cheapest way for a bot to keep Postgres busy. Queries are now cut
at eight words (more never improve the answer), each address gets
thirty searches and then thirty a minute, and the statement is
cancelled after five seconds; a timeout reads as no results and is
logged, rather than a 500.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
Diffstat (limited to 'internal/web/search.go')
| -rw-r--r-- | internal/web/search.go | 10 |
1 files changed, 8 insertions, 2 deletions
diff --git a/internal/web/search.go b/internal/web/search.go index 9af980d..e0c3533 100644 --- a/internal/web/search.go +++ b/internal/web/search.go @@ -5,6 +5,7 @@ import ( "html/template" "regexp" "strings" + "time" "unicode/utf8" "github.com/microcosm-cc/bluemonday" @@ -14,8 +15,10 @@ import ( const ( searchPerPage = 20 - maxSearchRunes = 100 // longer queries are cut; nobody types more on purpose - snippetContext = 80 // runes kept on each side of the match + maxSearchRunes = 100 // longer queries are cut; nobody types more on purpose + maxSearchWords = 8 // more ".*" joins only make the regex scan dearer, never the answer better + snippetContext = 80 // runes kept on each side of the match + searchDeadline = 5 * time.Second // the scan is unindexed; past this it is abuse or a blog too big for it ) // searchPattern turns what the reader typed into the regular expression both @@ -23,6 +26,9 @@ const ( // in between", so "go tem" finds "Go templates". Blank → "". func searchPattern(q string) string { words := strings.Fields(q) + if len(words) > maxSearchWords { + words = words[:maxSearchWords] + } for i, w := range words { words[i] = regexp.QuoteMeta(w) } |
