From 07fbe05ece75ee4fa001b8904ee34d508354d08d Mon Sep 17 00:00:00 2001 From: Anric Date: Fri, 13 Mar 2026 11:57:22 +0800 Subject: [PATCH] feat(config): add allow_private_hosts option to control access to local/private IPs --- config/config.example.json | 3 ++- pkg/agent/loop.go | 3 +++ pkg/config/config.go | 3 +++ pkg/config/defaults.go | 1 + pkg/tools/web.go | 6 ++++++ 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/config/config.example.json b/config/config.example.json index b259df6f6..349a5e964 100644 --- a/config/config.example.json +++ b/config/config.example.json @@ -328,7 +328,8 @@ "search_engine": "search_std", "max_results": 5 }, - "fetch_limit_bytes": 10485760 + "fetch_limit_bytes": 10485760, + "allow_private_hosts": false }, "cron": { "enabled": true, diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 28e549ce0..73ed23fde 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -114,6 +114,9 @@ func registerSharedTools( registry *AgentRegistry, provider providers.LLMProvider, ) { + // Apply global settings that affect tool behavior. + tools.SetAllowPrivateWebFetchHosts(cfg.Tools.Web.AllowPrivateHosts) + for _, agentID := range registry.ListAgentIDs() { agent, ok := registry.GetAgent(agentID) if !ok { diff --git a/pkg/config/config.go b/pkg/config/config.go index 7a7edb489..3df5a2778 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -692,6 +692,9 @@ type WebToolsConfig struct { // For authenticated proxies, prefer HTTP_PROXY/HTTPS_PROXY env vars instead of embedding credentials in config. Proxy string `json:"proxy,omitempty" env:"PICOCLAW_TOOLS_WEB_PROXY"` FetchLimitBytes int64 `json:"fetch_limit_bytes,omitempty" env:"PICOCLAW_TOOLS_WEB_FETCH_LIMIT_BYTES"` + // AllowPrivateHosts controls whether web_fetch may connect to local/private IPs. + // Defaults to false to reduce SSRF exposure. + AllowPrivateHosts bool `json:"allow_private_hosts" env:"PICOCLAW_TOOLS_WEB_ALLOW_PRIVATE_HOSTS"` } type CronToolsConfig struct { diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index 2a3e66043..0cb7cb716 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -395,6 +395,7 @@ func DefaultConfig() *Config { }, Proxy: "", FetchLimitBytes: 10 * 1024 * 1024, // 10MB by default + AllowPrivateHosts: false, Brave: BraveConfig{ Enabled: false, APIKey: "", diff --git a/pkg/tools/web.go b/pkg/tools/web.go index 003cd860c..51eb1667e 100644 --- a/pkg/tools/web.go +++ b/pkg/tools/web.go @@ -823,6 +823,12 @@ func NewWebFetchTool(maxChars int, fetchLimitBytes int64) (*WebFetchTool, error) // This is false in normal runtime to reduce SSRF exposure, and tests can override it temporarily. var allowPrivateWebFetchHosts atomic.Bool +// SetAllowPrivateWebFetchHosts configures whether the web_fetch tool may access local/private IPs. +// This is normally false to reduce SSRF attack surface. +func SetAllowPrivateWebFetchHosts(allow bool) { + allowPrivateWebFetchHosts.Store(allow) +} + func NewWebFetchToolWithProxy(maxChars int, proxy string, fetchLimitBytes int64) (*WebFetchTool, error) { if maxChars <= 0 { maxChars = defaultMaxChars