mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-08-27 22:35:21 +00:00
fix(routes): stop leaking apiKey presence via writeEnabled on public GET
PR review found GET /api/config/geo-filter is unauthenticated and was returning writeEnabled (derived from APIKey != "" && !IsWeakAPIKey). That tells any anonymous caller whether a strong API key is configured on the server — low-severity info disclosure, but free to fix. Approach: drop the field entirely instead of gating the endpoint behind requireAPIKey. The polygon itself is intentionally public (read-only clients depend on it), so auth-gating the whole endpoint would break them. Clients that want to write should just attempt PUT and handle 401/403 — the customizer JS already has that error path. - routes.go: remove writeEnabled from both response branches - customize-v2.js: always reveal edit controls; rely on PUT error path - docs/user-guide/geofilter.md: drop the writeEnabled API doc + the "controls only appear when..." UX line Green commit for the red test in the previous commit.
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user