aboutsummaryrefslogtreecommitdiffstats
path: root/internal/web/server.go
diff options
context:
space:
mode:
authorgrm <grm@eyesin.space>2026-09-18 13:50:35 +0300
committergrm <grm@eyesin.space>2026-09-18 13:50:42 +0300
commitc3026c34b042cc044cddfc5674d5f5ad69bb845d (patch)
tree7111e8fc310b193c510110e813befb00695a7597 /internal/web/server.go
parentc47397ac1e2ceafafe2be3cdec86366dd396ed6f (diff)
downloadblogspace-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/server.go')
-rw-r--r--internal/web/server.go5
1 files changed, 3 insertions, 2 deletions
diff --git a/internal/web/server.go b/internal/web/server.go
index 6009310..f02dd68 100644
--- a/internal/web/server.go
+++ b/internal/web/server.go
@@ -26,14 +26,15 @@ type Server struct {
root http.Handler
blog http.Handler
// Anonymous endpoints worth abusing get a token bucket each (ratelimit.go).
- loginLimit *limiter // per client address and per username: 10 guesses, then 10 a minute
+ loginLimit *limiter // per client address and per username: 10 guesses, then 10 a minute
+ searchLimit *limiter // per client address: the search is an unindexed regex scan
}
// logf is log.Printf, a variable so tests can silence it.
var logf = log.Printf
func NewServer(cfg *config.Config, st *store.Store) *Server {
- s := &Server{cfg: cfg, st: st, tpl: newTemplates(cfg.Dev), loginLimit: newLimiter(10, 10)}
+ s := &Server{cfg: cfg, st: st, tpl: newTemplates(cfg.Dev), loginLimit: newLimiter(10, 10), searchLimit: newLimiter(30, 30)}
s.root = s.rootRoutes()
s.blog = s.subdomainRoutes()
return s