Files
forge/apps/api/tests/test_service_token_egress.py
T
nihalashetty ae67bff5a3 feat: deep-agent canvas, live observability, and multi-environment tooling
Self-hosted platform for building, testing, and shipping LangChain/LangGraph agents. Deep-agent sub-agents on the canvas, a live tracing/observability timeline, auto-provisioned built-in tools with import/export, per-environment tool variables, streamed evaluations, and per-user auth token forwarding.
2026-07-28 01:49:19 +05:30

117 lines
4.8 KiB
Python

"""Server-to-server integration primitives:
1. A static service API token (`FORGE_SERVICE_API_TOKEN`) that authenticates a trusted backend
as a least-privilege (editor) service identity - the outer "is this call from our backend"
barrier. Non-expiring, revoked by rotation.
2. An egress allow-private-hosts toggle (`FORGE_EGRESS_ALLOW_PRIVATE_HOSTS`) that lets specific
trusted internal hosts (localhost, on-prem services) be reached even while the SSRF guard's
private-address block stays on globally (default-deny, explicit-allow).
"""
import pytest
from fastapi import HTTPException
from forge.config import settings
from forge.deps import get_current_user
from forge.util.ssrf import EgressBlocked, EgressPolicy, validate_url
# --- service token ---------------------------------------------------------------------------
class _State:
tenant_id = "t-seed"
class _App:
state = _State()
class _Req:
def __init__(self, authorization: str | None):
self.headers = {"authorization": authorization} if authorization else {}
self.app = _App()
async def test_service_token_authenticates_as_editor(monkeypatch):
monkeypatch.setattr(settings, "service_api_token", "svc-secret-abc123")
user = await get_current_user(_Req("Bearer svc-secret-abc123"))
assert user.role == "editor" # least privilege, but enough to assert end_user identity
assert user.tenant_id == "t-seed" # bound to the seeded workspace
assert user.is_fallback is False
async def test_wrong_service_token_is_rejected(monkeypatch):
# A present-but-wrong token must NOT fall back to an anonymous/owner identity: it fails the
# constant-time compare, then fails JWT decode -> 401.
monkeypatch.setattr(settings, "service_api_token", "svc-secret-abc123")
with pytest.raises(HTTPException) as e:
await get_current_user(_Req("Bearer not-the-token"))
assert e.value.status_code == 401
async def test_empty_service_token_setting_is_disabled(monkeypatch):
# With no service token configured, the branch is inert (a random bearer is treated as a
# would-be JWT and rejected).
monkeypatch.setattr(settings, "service_api_token", "")
with pytest.raises(HTTPException) as e:
await get_current_user(_Req("Bearer anything"))
assert e.value.status_code == 401
# --- egress allow-private-hosts --------------------------------------------------------------
async def test_private_host_blocked_by_default():
with pytest.raises(EgressBlocked):
await validate_url("http://127.0.0.1:9002/x", EgressPolicy(block_private=True))
async def test_allow_private_hosts_bypasses_block_by_ip():
url = await validate_url(
"http://127.0.0.1:9002/x",
EgressPolicy(block_private=True, allow_private_hosts=("127.0.0.1",)),
)
assert url == "http://127.0.0.1:9002/x"
async def test_allow_private_hosts_bypasses_block_by_name():
# `localhost` resolves to a loopback IP, but the host is explicitly allow-listed.
url = await validate_url(
"http://localhost:9002/orders/detail/1",
EgressPolicy(block_private=True, allow_private_hosts=("localhost",)),
)
assert url.startswith("http://localhost")
async def test_non_allowlisted_private_still_blocked():
with pytest.raises(EgressBlocked):
await validate_url(
"http://10.0.0.5/x",
EgressPolicy(block_private=True, allow_private_hosts=("localhost",)),
)
def test_from_settings_reads_global_allow_private(monkeypatch):
monkeypatch.setattr(settings, "egress_allow_private_hosts", ["localhost", "127.0.0.1"])
p = EgressPolicy.from_settings()
assert "localhost" in p.allow_private_hosts and "127.0.0.1" in p.allow_private_hosts
def test_from_settings_ignores_project_allow_private(monkeypatch):
# SECURITY (audit H1): project config is editable by any tenant member, so it must NOT be able
# to add a private-host bypass. allow_private_hosts is a deployment-level control only.
monkeypatch.setattr(settings, "egress_allow_private_hosts", [])
p = EgressPolicy.from_settings({"allow_private_hosts": ["10.0.0.5"]})
assert "10.0.0.5" not in p.allow_private_hosts
assert p.allow_private_hosts == ()
def test_from_settings_project_cannot_loosen_block_private(monkeypatch):
# SECURITY (audit H1): a project may only TIGHTEN block_private (False->True), never turn the
# SSRF guard off. With the guard on globally, project block_private:false is ignored.
monkeypatch.setattr(settings, "egress_block_private", True)
assert EgressPolicy.from_settings({"block_private": False}).block_private is True
# And a project can still tighten when the deployment default is off.
monkeypatch.setattr(settings, "egress_block_private", False)
assert EgressPolicy.from_settings({"block_private": True}).block_private is True