From bc683bef39f0c8c4d1a259415a9b325cfd8795ba Mon Sep 17 00:00:00 2001 From: Christine Stirrat Date: Mon, 11 May 2026 11:56:09 -0400 Subject: [PATCH] fix: allow additional trusted Feature Service hosts for ArcGIS plugin --- cli/commands/configure.py | 6 ++++++ plugins/arcgis/config_schema.py | 6 +++++- plugins/arcgis/plugin.py | 8 +++++--- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/cli/commands/configure.py b/cli/commands/configure.py index 1d0464d..823f021 100644 --- a/cli/commands/configure.py +++ b/cli/commands/configure.py @@ -183,6 +183,12 @@ def _prompt_plugin_config(plugin: str, defaults: dict) -> dict: default=str(plugin_defaults.get("timeout", 120)), ).ask() cfg["timeout"] = int(timeout) + extra_hosts_str = questionary.text( + "Additional trusted Feature Service hosts (comma-separated, or leave blank):", + default="", + ).ask() + if extra_hosts_str: + cfg["allowed_hosts"] = [h.strip() for h in extra_hosts_str.split(",") if h.strip()] # Abort if any prompt was cancelled (Ctrl+C) for v in cfg.values(): diff --git a/plugins/arcgis/config_schema.py b/plugins/arcgis/config_schema.py index ab0636f..f8e914b 100644 --- a/plugins/arcgis/config_schema.py +++ b/plugins/arcgis/config_schema.py @@ -1,6 +1,6 @@ """Pydantic configuration schema for ArcGIS Hub plugin.""" -from typing import Optional +from typing import List, Optional from urllib.parse import urlparse from pydantic import BaseModel, ConfigDict, Field, field_validator @@ -24,6 +24,10 @@ class ArcGISPluginConfig(BaseModel): token: Optional[str] = Field( None, description="Optional Bearer token for authenticated requests" ) + allowed_hosts: List[str] = Field( + default_factory=list, + description="Additional Feature Service hostnames to trust (e.g. maps2.dcgis.dc.gov)", + ) @field_validator("portal_url") @classmethod diff --git a/plugins/arcgis/plugin.py b/plugins/arcgis/plugin.py index f8f17a4..9e26707 100644 --- a/plugins/arcgis/plugin.py +++ b/plugins/arcgis/plugin.py @@ -360,7 +360,7 @@ async def query_data( out_fields = filters.get("out_fields", "*") if filters else "*" service_url = self._ensure_layer_url(service_url) - self._validate_feature_url(service_url, self.plugin_config.portal_url) + self._validate_feature_url(service_url, self.plugin_config.portal_url, self.plugin_config.allowed_hosts) query_url = f"{service_url}/query" record_count = min(limit, 1000) params = { @@ -467,7 +467,7 @@ def _ensure_layer_url(service_url: str) -> str: return stripped @staticmethod - def _validate_feature_url(service_url: str, portal_url: str) -> str: + def _validate_feature_url(service_url: str, portal_url: str, allowed_hosts: list | None = None) -> str: parsed = urlparse(service_url) portal_netloc = urlparse(portal_url).netloc @@ -477,7 +477,9 @@ def _validate_feature_url(service_url: str, portal_url: str) -> str: ) host = parsed.netloc.lower() - if not (host.endswith(".arcgis.com") or host == portal_netloc.lower()): + extra = [h.lower() for h in (allowed_hosts or [])] + + if not (host.endswith(".arcgis.com") or host == portal_netloc.lower() or host in extra): raise ValueError( f"Feature service URL host {host!r} is not within allowed domains " f"(*.arcgis.com or {portal_netloc})"