Team Ai
Apppublic

booleanbeyond/jobfetch

sourceHugging Faceupdated 2mo agoView on Hugging Face
0likes
test_security.py121 linesDownload Raw Back to tests
1"""Egress guard tests. These are the ones that must never regress."""2 3from __future__ import annotations4 5import dataclasses6 7import pytest8 9from app import net as net_mod10from app import security as sec11from app.config import settings as base_settings12 13STRICT = dataclasses.replace(base_settings, allow_private_hosts=False, pin_dns=False)14 15 16@pytest.fixture(autouse=True)17def strict(monkeypatch):18    monkeypatch.setattr(sec, "settings", STRICT)19    monkeypatch.setattr(net_mod, "settings", STRICT)20 21 22@pytest.mark.asyncio23@pytest.mark.parametrize(24    "url",25    [26        "http://127.0.0.1/",27        "http://localhost/admin",28        "http://169.254.169.254/latest/meta-data/",29        "http://metadata.google.internal/computeMetadata/v1/",30        "http://[::1]:80/",31        "http://0.0.0.0/",32        "http://10.0.0.5/",33        "http://192.168.1.1/",34        "http://172.16.0.1/",35        "http://2130706433/",          # decimal 127.0.0.136        "http://0x7f.0x0.0x0.0x1/",    # hex 127.0.0.137        "http://127.1/",               # short form38    ],39)40async def test_private_and_obfuscated_targets_rejected(url):41    with pytest.raises(sec.UnsafeURLError):42        await sec.resolve_and_validate(url)43 44 45@pytest.mark.asyncio46@pytest.mark.parametrize(47    "url",48    [49        "file:///etc/passwd",50        "gopher://evil/",51        "ftp://files.example.com/x",52        "javascript:alert(1)",53        "data:text/html,<h1>x</h1>",54        "http://user:pass@example.com/",55        "http://example.com:22/",56        "",57        "   ",58    ],59)60async def test_bad_schemes_ports_and_credentials_rejected(url):61    with pytest.raises(sec.UnsafeURLError):62        await sec.resolve_and_validate(url)63 64 65def test_normalise_adds_scheme_and_drops_fragment():66    assert sec.normalise_url("example.com/careers#top") == "https://example.com/careers"67    assert sec.normalise_url("  https://a.com  ") == "https://a.com/"68 69 70def test_control_characters_rejected():71    with pytest.raises(sec.UnsafeURLError):72        sec.normalise_url("https://example.com/\nHost: evil")73 74 75@pytest.mark.asyncio76async def test_redirect_hops_are_revalidated(monkeypatch):77    """A public URL that 302s to link-local must be refused at the second hop."""78    budget = net_mod.Budget.start(10)79    client = net_mod.SafeClient(budget)80 81    hops: list[str] = []82 83    async def fake_raw(method, target, **kw):84        hops.append(target.url)85        return net_mod.Response(86            url=target.url,87            requested_url=target.url,88            status=302,89            headers={"location": "http://169.254.169.254/latest/meta-data/"},90            content=b"",91            text="",92            elapsed_ms=1,93        )94 95    async def fake_resolve(url):96        # Delegate to the real guard so the second hop is genuinely checked.97        if "169.254" in url:98            return await sec.resolve_and_validate(url)99        return sec.ValidatedTarget(url, "https", "example.com", 443, ("93.184.216.34",))100 101    monkeypatch.setattr(client, "_raw_request", fake_raw)102    monkeypatch.setattr(net_mod, "resolve_and_validate", fake_resolve)103 104    try:105        with pytest.raises(sec.UnsafeURLError):106            await client.get("https://example.com/start", check_robots=False)107        assert hops == ["https://example.com/start"]108    finally:109        await client.aclose()110 111 112@pytest.mark.asyncio113async def test_budget_stops_runaway_fetching():114    budget = net_mod.Budget.start(10)115    budget.max_fetches = 2116    budget.fetches = 2117    with pytest.raises(net_mod.BudgetExceeded):118        budget.check()119    assert budget.soft_ok() is False120    assert budget.truncated is True121