Skip to content

Commit dc962f9

Browse files
authored
fix(sidecar): stop leaking raw vector and SQL errors to clients (#1632)
* fix(sidecar): stop leaking raw vector and SQL errors to clients * fix: rename test secret vars to sensitive_path to avoid Ruff S105 Replace the variable name 'secret' with 'sensitive_path' in test_sql.py and test_vector.py to suppress Ruff S105 hardcoded-password-assigned lint warnings. * fix: use typed _boom helpers and assert exact error detail strings Replace lambdas/untyped helpers with _boom(*_args: object, **_kwargs: object) -> NoReturn in leak tests. Assert the exact generic detail string for each endpoint instead of a case-insensitive substring match.
1 parent a3781ce commit dc962f9

4 files changed

Lines changed: 73 additions & 3 deletions

File tree

backend/geolibre_server/geolibre_server/app/sql.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,7 @@ def sql_run(request: SqlRunRequest) -> dict[str, Any]:
8585
raise HTTPException(status_code=400, detail=str(exc)) from exc
8686
except Exception as exc: # noqa: BLE001 - surface a stable error to the client
8787
logger.exception("Sedona SQL failed")
88-
raise HTTPException(status_code=400, detail=f"Spatial SQL failed: {exc}") from exc
88+
raise HTTPException(
89+
status_code=400,
90+
detail="Spatial SQL failed due to an internal error.",
91+
) from exc

backend/geolibre_server/geolibre_server/app/vector.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,10 @@ def vector_run(request: VectorToolRequest):
106106
raise HTTPException(status_code=400, detail=str(exc)) from exc
107107
except Exception as exc: # noqa: BLE001 - surface a stable error to the client
108108
logger.exception("Vector tool %s failed", request.tool_id)
109-
raise HTTPException(status_code=400, detail=f"Vector tool failed: {exc}") from exc
109+
raise HTTPException(
110+
status_code=400,
111+
detail="Vector tool failed due to an internal error.",
112+
) from exc
110113

111114
return {"geojson": geojson, "messages": messages}
112115

@@ -298,7 +301,10 @@ def vector_write(request: WriteVectorRequest):
298301
raise
299302
except Exception as exc: # noqa: BLE001 - surface a stable error to the client
300303
logger.exception("Write-back to %s failed", target)
301-
raise HTTPException(status_code=400, detail=f"Write-back failed: {exc}") from exc
304+
raise HTTPException(
305+
status_code=400,
306+
detail="Write-back failed due to an internal error.",
307+
) from exc
302308

303309
return {
304310
"path": str(target),

backend/geolibre_server/tests/test_sql.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import NoReturn
2+
13
import pytest
24
from fastapi import HTTPException
35

@@ -213,3 +215,19 @@ def close(self):
213215

214216
with pytest.raises(SqlTimeout, match="timed out"):
215217
sedona_ops.run_sql("SELECT 1")
218+
219+
220+
def test_run_does_not_leak_exception_detail(monkeypatch: pytest.MonkeyPatch) -> None:
221+
"""Broad exceptions must not surface internal paths or secrets in the detail."""
222+
sensitive_path = "/var/data/credentials.json"
223+
monkeypatch.setattr(sedona_ops, "sedonadb_import_error", lambda: None)
224+
225+
def _boom(*_args: object, **_kwargs: object) -> NoReturn:
226+
raise RuntimeError(f"Failed to read {sensitive_path}") # noqa: TRY003
227+
228+
monkeypatch.setattr(sedona_ops, "run_sql", _boom)
229+
with pytest.raises(HTTPException) as exc:
230+
sql_run(SqlRunRequest(sql="SELECT 1"))
231+
assert exc.value.status_code == 400
232+
assert sensitive_path not in str(exc.value.detail)
233+
assert exc.value.detail == "Spatial SQL failed due to an internal error."

backend/geolibre_server/tests/test_vector.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import NoReturn
2+
13
import pytest
24
from fastapi import HTTPException
35

@@ -349,6 +351,47 @@ def test_write_geopackage_preserves_source_crs(tmp_path) -> None:
349351
assert reread.crs.to_epsg() == 3857
350352

351353

354+
@requires_geopandas
355+
def test_run_does_not_leak_exception_detail(monkeypatch: pytest.MonkeyPatch) -> None:
356+
"""Broad exceptions must not surface internal paths or secrets in the detail."""
357+
sensitive_path = "/var/secret/key.pem"
358+
monkeypatch.setattr(vector_ops, "geopandas_import_error", lambda: None)
359+
360+
def _boom(*_args: object, **_kwargs: object) -> NoReturn:
361+
raise RuntimeError(f"Cannot read {sensitive_path}") # noqa: TRY003
362+
363+
monkeypatch.setattr(vector_ops, "run_vector_tool", _boom)
364+
with pytest.raises(HTTPException) as exc:
365+
vector_run(VectorToolRequest(tool_id="buffer", geojson=SQUARE))
366+
assert exc.value.status_code == 400
367+
assert sensitive_path not in str(exc.value.detail)
368+
assert exc.value.detail == "Vector tool failed due to an internal error."
369+
370+
371+
@requires_geopandas
372+
def test_write_does_not_leak_exception_detail(tmp_path, monkeypatch: pytest.MonkeyPatch) -> None:
373+
"""Write-back broad exceptions must not surface internal detail."""
374+
import geopandas as gpd
375+
376+
from geolibre_server.app import vector as vector_mod
377+
378+
src = tmp_path / "layer.geojson"
379+
gpd.GeoDataFrame.from_features(_edited("a")["features"], crs="EPSG:4326").to_file(
380+
src, driver="GeoJSON"
381+
)
382+
sensitive_path = "/etc/shadow"
383+
384+
def _boom(*_args: object, **_kwargs: object) -> NoReturn:
385+
raise RuntimeError(f"leaked {sensitive_path}") # noqa: TRY003
386+
387+
monkeypatch.setattr(vector_mod, "_write_geojson", _boom)
388+
with pytest.raises(HTTPException) as exc:
389+
vector_write(WriteVectorRequest(path=str(src), geojson=_edited("b")))
390+
assert exc.value.status_code == 400
391+
assert sensitive_path not in str(exc.value.detail)
392+
assert exc.value.detail == "Write-back failed due to an internal error."
393+
394+
352395
@requires_geopandas
353396
def test_write_respects_conversion_allowlist(tmp_path, monkeypatch: pytest.MonkeyPatch) -> None:
354397
import geopandas as gpd

0 commit comments

Comments
 (0)