Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions web/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1058,11 +1058,20 @@
# This prevents SSRF attacks via user-controlled API URL fields.
# Set to an empty list to disable URL restriction (not recommended).
# Add entries for custom providers (LiteLLM, LM Studio, corporate proxies).
#
# Use ``:*`` to match any port on a host — useful for local self-hosting
# where the user chooses the port. The host check still rejects
# link-local addresses like 169.254.169.254 used by cloud metadata.
ALLOWED_LLM_API_URLS = [
'https://api.anthropic.com:443',
'https://api.openai.com:443',
'http://localhost:11434', # Ollama default
'http://localhost:12434', # Docker Model Runner default
# Loopback addresses on any port: covers Ollama (11434), Docker
# Model Runner (12434), LiteLLM (4000), vLLM (8000), LM Studio
# (1234), text-generation-webui (5000), and any self-hosted
# OpenAI-compatible endpoint the user runs on their own machine.
'http://localhost:*',
'http://127.0.0.1:*',
'http://[::1]:*',
]

# Maximum Tool Iterations
Expand Down
38 changes: 37 additions & 1 deletion web/pgadmin/llm/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ def get_llm_client(
get_anthropic_api_url, get_anthropic_api_key, get_anthropic_model,
get_openai_api_url, get_openai_api_key, get_openai_model,
get_ollama_api_url, get_ollama_model,
get_docker_api_url, get_docker_model
get_docker_api_url, get_docker_model,
is_pref_api_url_rejected,
is_pref_api_key_path_rejected,
)

# Determine which provider to use
Expand All @@ -183,8 +185,34 @@ def get_llm_client(

provider = provider.lower()

def _rejected_url_error(prov):
return LLMClientError(LLMError(
message=(
f"The configured {prov} API URL is not in the "
"allowed list (ALLOWED_LLM_API_URLS). Add it to "
"config_local.py, or clear the API URL preference "
"to use the system default."
),
provider=prov,
))

def _rejected_key_file_error(prov):
return LLMClientError(LLMError(
message=(
f"The configured {prov} API key file is not within "
"your private user storage. Move the key file to "
"your private storage directory, or clear the API "
"Key File preference to use the system default."
),
provider=prov,
))

if provider == 'anthropic':
from pgadmin.llm.providers.anthropic import AnthropicClient
if is_pref_api_url_rejected('anthropic_api_url'):
raise _rejected_url_error('anthropic')
if is_pref_api_key_path_rejected('anthropic_api_key_file'):
raise _rejected_key_file_error('anthropic')
api_key = get_anthropic_api_key()
api_url = get_anthropic_api_url()
if not api_key and not api_url:
Expand All @@ -199,6 +227,10 @@ def get_llm_client(

elif provider == 'openai':
from pgadmin.llm.providers.openai import OpenAIClient
if is_pref_api_url_rejected('openai_api_url'):
raise _rejected_url_error('openai')
if is_pref_api_key_path_rejected('openai_api_key_file'):
raise _rejected_key_file_error('openai')
api_key = get_openai_api_key()
api_url = get_openai_api_url()
if not api_key and not api_url:
Expand All @@ -213,6 +245,8 @@ def get_llm_client(

elif provider == 'ollama':
from pgadmin.llm.providers.ollama import OllamaClient
if is_pref_api_url_rejected('ollama_api_url'):
raise _rejected_url_error('ollama')
api_url = get_ollama_api_url()
if not api_url:
raise LLMClientError(LLMError(
Expand All @@ -224,6 +258,8 @@ def get_llm_client(

elif provider == 'docker':
from pgadmin.llm.providers.docker import DockerClient
if is_pref_api_url_rejected('docker_api_url'):
raise _rejected_url_error('docker')
api_url = get_docker_api_url()
if not api_url:
raise LLMClientError(LLMError(
Expand Down
Loading
Loading