mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-04-20 09:59:54 +00:00
fix: reset ssrf whitelist on config reload and document config refresh
This commit is contained in:
parent
5f08d61d8f
commit
9ef5b1e145
15
README.md
15
README.md
@ -856,6 +856,11 @@ Simply send the command above to your nanobot (via CLI or any chat channel), and
|
|||||||
|
|
||||||
Config file: `~/.nanobot/config.json`
|
Config file: `~/.nanobot/config.json`
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> If your config file is older than the current schema, you can refresh it without overwriting your existing values:
|
||||||
|
> run `nanobot onboard`, then answer `N` when asked whether to overwrite the config.
|
||||||
|
> nanobot will merge in missing default fields and keep your current settings.
|
||||||
|
|
||||||
### Providers
|
### Providers
|
||||||
|
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
@ -1235,6 +1240,16 @@ By default, web tools are enabled and web search uses `duckduckgo`, so search wo
|
|||||||
|
|
||||||
If you want to disable all built-in web tools entirely, set `tools.web.enable` to `false`. This removes both `web_search` and `web_fetch` from the tool list sent to the LLM.
|
If you want to disable all built-in web tools entirely, set `tools.web.enable` to `false`. This removes both `web_search` and `web_fetch` from the tool list sent to the LLM.
|
||||||
|
|
||||||
|
If you need to allow trusted private ranges such as Tailscale / CGNAT addresses, you can explicitly exempt them from SSRF blocking with `tools.ssrfWhitelist`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tools": {
|
||||||
|
"ssrfWhitelist": ["100.64.0.0/10"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
| Provider | Config fields | Env var fallback | Free |
|
| Provider | Config fields | Env var fallback | Free |
|
||||||
|----------|--------------|------------------|------|
|
|----------|--------------|------------------|------|
|
||||||
| `brave` | `apiKey` | `BRAVE_API_KEY` | No |
|
| `brave` | `apiKey` | `BRAVE_API_KEY` | No |
|
||||||
|
|||||||
@ -54,10 +54,9 @@ def load_config(config_path: Path | None = None) -> Config:
|
|||||||
|
|
||||||
def _apply_ssrf_whitelist(config: Config) -> None:
|
def _apply_ssrf_whitelist(config: Config) -> None:
|
||||||
"""Apply SSRF whitelist from config to the network security module."""
|
"""Apply SSRF whitelist from config to the network security module."""
|
||||||
if config.tools.ssrf_whitelist:
|
from nanobot.security.network import configure_ssrf_whitelist
|
||||||
from nanobot.security.network import configure_ssrf_whitelist
|
|
||||||
|
|
||||||
configure_ssrf_whitelist(config.tools.ssrf_whitelist)
|
configure_ssrf_whitelist(config.tools.ssrf_whitelist)
|
||||||
|
|
||||||
|
|
||||||
def save_config(config: Config, config_path: Path | None = None) -> None:
|
def save_config(config: Config, config_path: Path | None = None) -> None:
|
||||||
|
|||||||
@ -1,6 +1,18 @@
|
|||||||
import json
|
import json
|
||||||
|
import socket
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
from nanobot.config.loader import load_config, save_config
|
from nanobot.config.loader import load_config, save_config
|
||||||
|
from nanobot.security.network import validate_url_target
|
||||||
|
|
||||||
|
|
||||||
|
def _fake_resolve(host: str, results: list[str]):
|
||||||
|
"""Return a getaddrinfo mock that maps the given host to fake IP results."""
|
||||||
|
def _resolver(hostname, port, family=0, type_=0):
|
||||||
|
if hostname == host:
|
||||||
|
return [(socket.AF_INET, socket.SOCK_STREAM, 0, "", (ip, 0)) for ip in results]
|
||||||
|
raise socket.gaierror(f"cannot resolve {hostname}")
|
||||||
|
return _resolver
|
||||||
|
|
||||||
|
|
||||||
def test_load_config_keeps_max_tokens_and_ignores_legacy_memory_window(tmp_path) -> None:
|
def test_load_config_keeps_max_tokens_and_ignores_legacy_memory_window(tmp_path) -> None:
|
||||||
@ -126,3 +138,23 @@ def test_onboard_refresh_backfills_missing_channel_fields(tmp_path, monkeypatch)
|
|||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
saved = json.loads(config_path.read_text(encoding="utf-8"))
|
saved = json.loads(config_path.read_text(encoding="utf-8"))
|
||||||
assert saved["channels"]["qq"]["msgFormat"] == "plain"
|
assert saved["channels"]["qq"]["msgFormat"] == "plain"
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_config_resets_ssrf_whitelist_when_next_config_is_empty(tmp_path) -> None:
|
||||||
|
whitelisted = tmp_path / "whitelisted.json"
|
||||||
|
whitelisted.write_text(
|
||||||
|
json.dumps({"tools": {"ssrfWhitelist": ["100.64.0.0/10"]}}),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
defaulted = tmp_path / "defaulted.json"
|
||||||
|
defaulted.write_text(json.dumps({}), encoding="utf-8")
|
||||||
|
|
||||||
|
load_config(whitelisted)
|
||||||
|
with patch("nanobot.security.network.socket.getaddrinfo", _fake_resolve("ts.local", ["100.100.1.1"])):
|
||||||
|
ok, err = validate_url_target("http://ts.local/api")
|
||||||
|
assert ok, err
|
||||||
|
|
||||||
|
load_config(defaulted)
|
||||||
|
with patch("nanobot.security.network.socket.getaddrinfo", _fake_resolve("ts.local", ["100.100.1.1"])):
|
||||||
|
ok, _ = validate_url_target("http://ts.local/api")
|
||||||
|
assert not ok
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user