check_run:
  id: OBS-002
  status: partial
  evidence_collected: 5
  evidence_required: 2
  findings:
    - "Central masking exists and is correct for the generic path: src/swisstopo_mcp/api_client.py:384-414 classifies HTTP status / timeout / connect errors into fixed German messages and, for anything unexpected, logs the detail to stderr (api_client.py:413) and returns only \"Unerwarteter interner Fehler. Bitte später erneut versuchen.\" (api_client.py:414). No traceback.format_exc()/sys.exc_info() anywhere in src/."
    - "LEAK — raw upstream body reaches the user: src/swisstopo_mcp/overpass.py:145-146 falls back to `return text.strip()[:300]` on any body containing the substring \"error\", and that string is interpolated straight into the tool summary at overpass.py:176 (`f\"Overpass-Fehler: {err}\"`). Executed against a realistic Overpass error page, the tool summary came back containing the server-side filesystem path `/opt/osm/db/overpass_db`, the RAM figure and the full submitted Overpass query with the user's coordinates."
    - "LEAK — internal egress configuration reaches the user: src/swisstopo_mcp/api_client.py:133-136 raises PermissionError whose message embeds `sorted(ALLOWED_HOSTS)`; handle_api_error treats PermissionError as a user-facing validation error (api_client.py:407-409, `return f\"{prefix}{e}\"`), so a blocked request hands the LLM the server's complete ten-host egress allow-list. api_client.py:115-118 similarly returns the resolved internal IP address."
    - "Argument validation errors are returned verbatim by the SDK: runtime probe of tools/call swisstopo_geocode with an empty params object returned \"Error executing tool swisstopo_geocode: 1 validation error for swisstopo_geocodeArguments … https://errors.pydantic.dev/2.13/v/missing\" — internal model name and dependency version disclosed. Not the server's code, but `mask_error_details` is not available to mitigate it (see gaps)."
    - "Masking is regression-tested: tests/test_api_client.py:80-82 asserts a RuntimeError produces \"Unerwarteter interner Fehler\" and tests/test_api_client.py:86 asserts intentional ValueError guidance survives."
  gaps:
    - "overpass.py:146 must not return the raw body; the 300-char fallback should be dropped or replaced with a fixed message, with the body logged to stderr only."
    - "PermissionError messages should not travel to the LLM verbatim — the allow-list and resolved IP belong in the log, not the tool result."
    - "mask_error_details=True is not set on the FastMCP init (src/swisstopo_mcp/server.py:49-62). Verified against the installed SDK: mcp.server.fastmcp.FastMCP.__init__ has no such parameter (mcp 1.28.1), so this pass criterion is not achievable without switching to the standalone fastmcp package. Handled defence-in-depth by the try/except-everything pattern instead."
  evaluator_notes: |
    The remediation's core claim — unexpected exceptions are masked — is real
    and tested. But the check asks whether ANY upstream body or URL can reach
    a user-facing message, and two paths do. The Overpass one is the serious
    one: it is an unconditional passthrough of up to 300 characters of a
    third-party HTML body, and a realistic Overpass error page contains a
    server filesystem path plus the echoed query. The egress-allow-list
    disclosure is lower impact (the hosts are public federal endpoints) but is
    still internal configuration leaving the trust boundary on a provokable
    error. Partial.
