guard knowledge base URL import against SSRF and fix OIDC transport defaults

The earlier SSRF work covered the provider, custom-tool, webhook, and OIDC
outbound calls but missed the knowledge base URL import path, which still used
http.DefaultClient. An admin importing a URL could reach loopback, link-local,
or RFC1918 hosts. Renamed the Manager's toolClient to httpClient (it is a
general SSRF-guarded outbound client now) and pointed fetchURL at it. The guard
sits on the dialer's Control, so it also runs on redirect targets, not just the
first hop.

Also fixed the OIDC discovery client. Its hand-built transport dropped two
defaults that http.DefaultTransport sets. Added Proxy: http.ProxyFromEnvironment
so it honors HTTP_PROXY/HTTPS_PROXY (SSO discovery now works behind an egress
proxy) and ForceAttemptHTTP2 so HTTP/2 is negotiated.
This commit is contained in:
Abhinav Raut
2026-07-17 19:24:31 +05:30
parent eed50ef602
commit 758a01ab74
4 changed files with 6 additions and 4 deletions
+2 -2
View File
@@ -50,7 +50,7 @@ type Manager struct {
snippetGenMu sync.Mutex
snippetGen map[int]uint64
dialControl ssrf.Control
toolClient *http.Client
httpClient *http.Client
}
// Opts contains options for initializing the Manager.
@@ -111,7 +111,7 @@ func New(opts Opts) (*Manager, error) {
index: newEmbeddingIndex(),
snippetGen: make(map[int]uint64),
dialControl: opts.DialControl,
toolClient: &http.Client{
httpClient: &http.Client{
Timeout: 20 * time.Second,
Transport: &http.Transport{
DialContext: (&net.Dialer{
+1 -1
View File
@@ -247,7 +247,7 @@ func (m *Manager) buildToolRegistry(tctx ToolContext, allowedToolIDs []int, incl
m.lo.Warn("skipping custom tool that collides with a built-in tool", "name", ct.Name)
continue
}
ht := newHTTPTool(ct, m.encryptionKey, m.lo, m.toolClient, tctx)
ht := newHTTPTool(ct, m.encryptionKey, m.lo, m.httpClient, tctx)
registry[ht.Name()] = ht
defs = append(defs, toolDef(ht))
}
+1 -1
View File
@@ -56,7 +56,7 @@ func (m *Manager) fetchURL(ctx context.Context, pageURL string) (string, string,
return "", "", err
}
req.Header.Set("User-Agent", "libredesk")
resp, err := http.DefaultClient.Do(req)
resp, err := m.httpClient.Do(req)
if err != nil {
return "", "", err
}
+2
View File
@@ -137,11 +137,13 @@ func newOIDCClient(dialControl ssrf.Control) *http.Client {
return &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 3 * time.Second,
KeepAlive: 30 * time.Second,
Control: dialControl,
}).DialContext,
ForceAttemptHTTP2: true,
TLSHandshakeTimeout: 5 * time.Second,
ResponseHeaderTimeout: 5 * time.Second,
},