diff --git a/cmd/server/routes.go b/cmd/server/routes.go index 3fb146b5..b72b06e2 100644 --- a/cmd/server/routes.go +++ b/cmd/server/routes.go @@ -467,15 +467,15 @@ func (s *Server) handleConfigMap(w http.ResponseWriter, r *http.Request) { func (s *Server) handleConfigGeoFilter(w http.ResponseWriter, r *http.Request) { gf := s.getGeoFilter() - // writeEnabled leaks whether the server has a strong API key to unauthenticated - // callers. Risk accepted: the information (key is/isn't configured) is low-sensitivity - // and the GET endpoint is intentionally public for read-only clients. - writeEnabled := s.cfg != nil && s.cfg.APIKey != "" && !IsWeakAPIKey(s.cfg.APIKey) + // NOTE: do NOT include any field that derives from APIKey presence/strength here. + // This endpoint is intentionally public; leaking whether a write-capable key is + // configured is an info-disclosure. Clients that want to write should just try + // PUT and handle 401/403. See PR #736 review. if gf == nil || len(gf.Polygon) == 0 { - writeJSON(w, map[string]interface{}{"polygon": nil, "bufferKm": 0, "writeEnabled": writeEnabled}) + writeJSON(w, map[string]interface{}{"polygon": nil, "bufferKm": 0}) return } - writeJSON(w, map[string]interface{}{"polygon": gf.Polygon, "bufferKm": gf.BufferKm, "writeEnabled": writeEnabled}) + writeJSON(w, map[string]interface{}{"polygon": gf.Polygon, "bufferKm": gf.BufferKm}) } func (s *Server) handlePutConfigGeoFilter(w http.ResponseWriter, r *http.Request) { diff --git a/docs/user-guide/geofilter.md b/docs/user-guide/geofilter.md index c61852d6..f17d7a62 100644 --- a/docs/user-guide/geofilter.md +++ b/docs/user-guide/geofilter.md @@ -62,7 +62,7 @@ If your server has an `apiKey` configured, the **GeoFilter tab** in the Customiz 5. Enter your **Server API Key** (the `apiKey` value from `config.json`) 6. Click **Save to server** — the filter is applied immediately, no restart needed -The editing controls only appear when the server has a write-capable API key configured. On deployments without an `apiKey`, the tab shows the current polygon as read-only. +The editing controls are always visible. Saving requires entering the server's `apiKey`; on servers without one (or with a weak key), the save request returns `401`/`403` and the error is shown inline. To remove the filter, click **Remove filter** (also requires the API key). @@ -86,7 +86,7 @@ For local/offline use without a running server, open `tools/geofilter-builder.ht GET /api/config/geo-filter ``` -Returns the current geo filter configuration. Also includes a `writeEnabled` boolean indicating whether the `PUT` endpoint is available (i.e., server has a write-capable `apiKey`). +Returns the current geo filter configuration (`polygon`, `bufferKm`). Whether the `PUT` endpoint will accept a write depends on whether the server has an `apiKey` configured; clients should attempt the write and handle `401`/`403` if it isn't. ``` PUT /api/config/geo-filter diff --git a/public/customize-v2.js b/public/customize-v2.js index 67a170e7..1e9ef4c9 100644 --- a/public/customize-v2.js +++ b/public/customize-v2.js @@ -1508,12 +1508,13 @@ if (!_gfLoaded) { api('/config/geo-filter', { ttl: 0 }).then(function (gf) { - // Show edit controls only on servers that have a write-capable API key configured - if (gf && gf.writeEnabled) { - _gfWriteEnabled = true; - var editEl = container.querySelector('#cv2-gf-edit'); - if (editEl) editEl.style.display = ''; - } + // Always expose edit controls. The server no longer leaks apiKey presence + // via writeEnabled (info-disclosure on a public endpoint); instead, the + // user enters their API key into the editor and writes either succeed or + // get a 401/403 surfaced as an inline error. + _gfWriteEnabled = true; + var editEl = container.querySelector('#cv2-gf-edit'); + if (editEl) editEl.style.display = ''; if (gf && gf.polygon && gf.polygon.length >= 3) { _gfPoints = gf.polygon.map(function (p) { return [p[0], p[1]]; }); var buf = container.querySelector('#cv2-gf-buffer'); @@ -1523,7 +1524,7 @@ _gfStatus(container, gf.polygon.length + ' points · bufferKm=' + (gf.bufferKm || 0)); } else { _gfPoints = []; - _gfStatus(container, gf && gf.writeEnabled ? 'No geo filter. Click the map to open the editor.' : 'No geo filter configured.'); + _gfStatus(container, 'No geo filter. Click the map to open the editor.'); _gfMap.setView([50.5, 4.4], 5); } _gfLoaded = true;