mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-12 07:09:19 +03:00
fix(providers): stop writing API keys into process os.environ
OpenAICompatProvider already passes api_key into AsyncOpenAI. Mutating shared os.environ leaked credentials across providers (gateway overwrite and setdefault first-writer-wins). Fixes #4784
This commit is contained in:
@@ -523,9 +523,6 @@ class OpenAICompatProvider(LLMProvider):
|
|||||||
self._proxy = proxy or None
|
self._proxy = proxy or None
|
||||||
self._native_compaction_available = True
|
self._native_compaction_available = True
|
||||||
|
|
||||||
if api_key and spec and spec.env_key:
|
|
||||||
self._setup_env(api_key, api_base)
|
|
||||||
|
|
||||||
effective_base = api_base or (spec.default_api_base if spec else None) or None
|
effective_base = api_base or (spec.default_api_base if spec else None) or None
|
||||||
self._effective_base = effective_base
|
self._effective_base = effective_base
|
||||||
self._default_headers = {"x-session-affinity": uuid.uuid4().hex}
|
self._default_headers = {"x-session-affinity": uuid.uuid4().hex}
|
||||||
@@ -618,20 +615,6 @@ class OpenAICompatProvider(LLMProvider):
|
|||||||
raise RuntimeError("OpenAI client initialization did not produce a client")
|
raise RuntimeError("OpenAI client initialization did not produce a client")
|
||||||
return self._client
|
return self._client
|
||||||
|
|
||||||
def _setup_env(self, api_key: str, api_base: str | None) -> None:
|
|
||||||
"""Set environment variables based on provider spec."""
|
|
||||||
spec = self._spec
|
|
||||||
if not spec or not spec.env_key:
|
|
||||||
return
|
|
||||||
if spec.is_gateway:
|
|
||||||
os.environ[spec.env_key] = api_key
|
|
||||||
else:
|
|
||||||
os.environ.setdefault(spec.env_key, api_key)
|
|
||||||
effective_base = api_base or spec.default_api_base
|
|
||||||
for env_name, env_val in spec.env_extras:
|
|
||||||
resolved = env_val.replace("{api_key}", api_key).replace("{api_base}", effective_base)
|
|
||||||
os.environ.setdefault(env_name, resolved)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _apply_cache_control(
|
def _apply_cache_control(
|
||||||
cls,
|
cls,
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
"""Provider credentials must not leak through process-global os.environ."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from nanobot.providers.openai_compat_provider import OpenAICompatProvider
|
||||||
|
from nanobot.providers.registry import find_by_name
|
||||||
|
|
||||||
|
|
||||||
|
def test_provider_init_does_not_mutate_shared_env_keys(monkeypatch) -> None:
|
||||||
|
"""Multi-provider setups must not overwrite or pin each other's keys."""
|
||||||
|
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
||||||
|
monkeypatch.delenv("OPENROUTER_API_KEY", raising=False)
|
||||||
|
|
||||||
|
openai_spec = find_by_name("openai")
|
||||||
|
openrouter_spec = find_by_name("openrouter")
|
||||||
|
assert openai_spec is not None and openrouter_spec is not None
|
||||||
|
|
||||||
|
OpenAICompatProvider(
|
||||||
|
api_key="sk-openai-secret",
|
||||||
|
default_model="gpt-4o",
|
||||||
|
spec=openai_spec,
|
||||||
|
)
|
||||||
|
OpenAICompatProvider(
|
||||||
|
api_key="sk-or-secret",
|
||||||
|
default_model="openrouter/auto",
|
||||||
|
spec=openrouter_spec,
|
||||||
|
api_base="https://openrouter.ai/api/v1",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "OPENAI_API_KEY" not in os.environ
|
||||||
|
assert "OPENROUTER_API_KEY" not in os.environ
|
||||||
|
|
||||||
|
|
||||||
|
def test_provider_init_preserves_preexisting_env_keys(monkeypatch) -> None:
|
||||||
|
monkeypatch.setenv("OPENAI_API_KEY", "preexisting-user-key")
|
||||||
|
|
||||||
|
openai_spec = find_by_name("openai")
|
||||||
|
assert openai_spec is not None
|
||||||
|
provider = OpenAICompatProvider(
|
||||||
|
api_key="sk-from-config",
|
||||||
|
default_model="gpt-4o",
|
||||||
|
spec=openai_spec,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert os.environ["OPENAI_API_KEY"] == "preexisting-user-key"
|
||||||
|
assert provider._api_key_for_client == "sk-from-config"
|
||||||
Reference in New Issue
Block a user