* Rewrite comments in Google developer documentation style Rewrite the comments and docstrings across the backend core modules so they read plainly. The previous prose was accurate but dense and figurative, which made it slow to skim. Applies the Google developer documentation style guide: short sentences, active voice, present tense, American spelling, and no metaphors, idioms, or rhetorical asides. Replaces em-dash chains with separate sentences.
70 lines
2.3 KiB
Python
70 lines
2.3 KiB
Python
"""Tests for the SSRF guard on the user-supplied BYOK endpoint_url.
|
|
|
|
python -m pytest tests/test_netguard.py -v
|
|
"""
|
|
import os
|
|
import tempfile
|
|
|
|
_tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False)
|
|
_tmp.close()
|
|
os.environ["AIDND_DB_PATH"] = _tmp.name
|
|
os.environ.pop("AIDND_DATABASE_URL", None)
|
|
os.environ.pop("DATABASE_URL", None)
|
|
|
|
import pytest
|
|
|
|
from app import auth, netguard
|
|
|
|
|
|
@pytest.fixture
|
|
def hosted(monkeypatch):
|
|
monkeypatch.setattr(auth, "MULTI_USER", True)
|
|
|
|
|
|
def _resolves_to(monkeypatch, ip: str):
|
|
"""Pin `getaddrinfo` so the test exercises the address decision, not real DNS."""
|
|
monkeypatch.setattr(
|
|
netguard.socket, "getaddrinfo",
|
|
lambda *a, **k: [(2, 1, 6, "", (ip, 443))],
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("ip", [
|
|
"127.0.0.1", # loopback
|
|
"169.254.169.254", # cloud metadata (link-local)
|
|
"10.0.0.5", # RFC1918
|
|
"192.168.1.1", # RFC1918
|
|
"172.16.0.9", # RFC1918
|
|
"0.0.0.0", # unspecified
|
|
"100.64.0.1", # carrier-grade NAT
|
|
"::1", # IPv6 loopback
|
|
"fd00::1", # IPv6 unique-local
|
|
])
|
|
def test_blocks_non_public_addresses(hosted, monkeypatch, ip):
|
|
_resolves_to(monkeypatch, ip)
|
|
assert netguard.endpoint_block_reason("https://evil.example.com/v1") is not None
|
|
|
|
|
|
def test_allows_public_address(hosted, monkeypatch):
|
|
_resolves_to(monkeypatch, "104.18.0.1") # a public IP
|
|
assert netguard.endpoint_block_reason("https://openrouter.ai/api/v1") is None
|
|
|
|
|
|
def test_rejects_non_http_scheme(hosted):
|
|
assert netguard.endpoint_block_reason("file:///etc/passwd") is not None
|
|
assert netguard.endpoint_block_reason("gopher://x/") is not None
|
|
|
|
|
|
def test_unresolvable_host_is_blocked(hosted, monkeypatch):
|
|
def boom(*a, **k):
|
|
raise netguard.socket.gaierror("no such host")
|
|
monkeypatch.setattr(netguard.socket, "getaddrinfo", boom)
|
|
assert netguard.endpoint_block_reason("https://nope.invalid/v1") is not None
|
|
|
|
|
|
def test_noop_in_local_mode(monkeypatch):
|
|
monkeypatch.setattr(auth, "MULTI_USER", False)
|
|
# Local installs must reach localhost (Ollama). The guard never blocks local mode.
|
|
assert netguard.endpoint_block_reason("http://localhost:11434/v1") is None
|
|
assert netguard.endpoint_block_reason("http://127.0.0.1:11434/v1") is None
|