forward-port: bound search response size with LimitReader

Re-applies the response-size limit from the deleted pkg/tools/web.go to
upstream's pkg/tools/integration/web.go. Wraps io.ReadAll on each search
provider's resp.Body with io.LimitReader against a named
searchMaxResponseSize constant (2 MB) to prevent unbounded memory growth
from hostile or buggy upstream search APIs.

Also normalises three existing inline `1<<20` (1 MB) limits upstream had
already applied to use the same named constant.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
admin-mf 2026-05-07 12:14:59 -05:00
parent 0c8ebde217
commit 94d28c1b86

View file

@ -34,6 +34,10 @@ const (
defaultMaxChars = 50000
maxRedirects = 5
// searchMaxResponseSize bounds in-memory reads of search-provider responses
// to prevent unbounded memory growth on hostile/buggy upstreams.
searchMaxResponseSize int64 = 2 << 20 // 2 MB
)
// Pre-compiled regexes for HTML text extraction
@ -293,7 +297,7 @@ func (p *BraveSearchProvider) Search(
continue
}
body, err := io.ReadAll(resp.Body)
body, err := io.ReadAll(io.LimitReader(resp.Body, searchMaxResponseSize))
resp.Body.Close()
if err != nil {
@ -413,7 +417,7 @@ func (p *TavilySearchProvider) Search(
continue
}
body, err := io.ReadAll(resp.Body)
body, err := io.ReadAll(io.LimitReader(resp.Body, searchMaxResponseSize))
resp.Body.Close()
if err != nil {
@ -501,7 +505,7 @@ func (p *SogouSearchProvider) Search(
return "", fmt.Errorf("request failed: %w", err)
}
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
body, err := io.ReadAll(io.LimitReader(resp.Body, searchMaxResponseSize))
resp.Body.Close()
if err != nil {
return "", fmt.Errorf("failed to read response: %w", err)
@ -594,7 +598,7 @@ func (p *DuckDuckGoSearchProvider) Search(
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
body, err := io.ReadAll(io.LimitReader(resp.Body, searchMaxResponseSize))
if err != nil {
return "", fmt.Errorf("failed to read response: %w", err)
}
@ -742,7 +746,7 @@ func (p *PerplexitySearchProvider) Search(
continue
}
body, err := io.ReadAll(resp.Body)
body, err := io.ReadAll(io.LimitReader(resp.Body, searchMaxResponseSize))
resp.Body.Close()
if err != nil {
@ -919,7 +923,7 @@ func (p *GLMSearchProvider) Search(
}
defer resp.Body.Close()
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
body, err := io.ReadAll(io.LimitReader(resp.Body, searchMaxResponseSize))
if err != nil {
return "", fmt.Errorf("failed to read response: %w", err)
}
@ -1015,7 +1019,7 @@ func (p *BaiduSearchProvider) Search(
}
defer resp.Body.Close()
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
body, err := io.ReadAll(io.LimitReader(resp.Body, searchMaxResponseSize))
if err != nil {
return "", fmt.Errorf("failed to read response: %w", err)
}