Skip to content

Commit 4a1a4e7

Browse files
committed
fix(providers): redact logged base url
1 parent 36198bd commit 4a1a4e7

2 files changed

Lines changed: 41 additions & 1 deletion

File tree

agent/src/providers/llm.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88
from pathlib import Path
99
from typing import Any, Dict, Optional
10+
from urllib.parse import urlsplit
1011

1112
try:
1213
from dotenv import load_dotenv
@@ -124,6 +125,33 @@ def _redact_env_source(loaded: Path | None) -> str:
124125
return "<.env>"
125126

126127

128+
def _redact_base_url_for_log(raw: str | None) -> str:
129+
"""Return a diagnostic-safe base URL label for logs."""
130+
if not raw or not raw.strip():
131+
return "(unset)"
132+
133+
try:
134+
parsed = urlsplit(raw.strip())
135+
except ValueError:
136+
return "<base-url>"
137+
138+
if not parsed.scheme or not parsed.hostname:
139+
return "<base-url>"
140+
141+
host = parsed.hostname
142+
if ":" in host and not host.startswith("["):
143+
host = f"[{host}]"
144+
145+
try:
146+
port = parsed.port
147+
except ValueError:
148+
port = None
149+
if port is not None:
150+
host = f"{host}:{port}"
151+
152+
return f"{parsed.scheme}://{host}"
153+
154+
127155
def _load_env_file(path: Path) -> None:
128156
"""Load a single .env file into os.environ (setdefault, no override)."""
129157
if load_dotenv is not None:
@@ -159,7 +187,7 @@ def _ensure_dotenv() -> None:
159187
_redact_env_source(loaded),
160188
os.getenv("LANGCHAIN_PROVIDER", "(unset)"),
161189
os.getenv("LANGCHAIN_MODEL_NAME", "(unset)"),
162-
os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "(unset)",
190+
_redact_base_url_for_log(os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE")),
163191
)
164192

165193

agent/tests/test_dotenv_observability.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,18 @@ def test_logs_none_when_no_env_found(tmp_path, fresh, monkeypatch, caplog):
7171
assert "none (no .env file found)" in msg
7272

7373

74+
def test_logs_redacted_base_url_without_credentials(tmp_path, fresh, monkeypatch, caplog):
75+
monkeypatch.setattr(llm, "_ENV_CANDIDATES", [tmp_path / "does-not-exist.env"])
76+
monkeypatch.setenv("OPENAI_BASE_URL", "https://sk-secret@example.com:8443/v1?api_key=sk-hidden")
77+
with caplog.at_level(logging.INFO, logger=LOGGER):
78+
llm._ensure_dotenv()
79+
msg = "\n".join(r.getMessage() for r in caplog.records)
80+
assert "base=https://example.com:8443" in msg
81+
assert "sk-secret" not in msg
82+
assert "sk-hidden" not in msg
83+
assert "api_key" not in msg
84+
85+
7486
def test_latch_still_skips_second_call(tmp_path, fresh, monkeypatch, caplog):
7587
"""Behavior preserved: still loads once per process (no log on re-entry)."""
7688
monkeypatch.setattr(llm, "_ENV_CANDIDATES", [tmp_path / "nope.env"])

0 commit comments

Comments
 (0)