You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a Security section to README and update CLAUDE.md: the HTTP transport now
requires CKAN_ALLOWED_DOMAINS (default-deny) and refuses to start without it
unless CKAN_HTTP_ALLOW_ALL=true. Fix the TRANSPORT=http examples that would
otherwise fail to start (v0.4.108).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@@ -424,7 +425,8 @@ To test with Claude Desktop, add MCP configuration to config file.
424
425
-**Caching**: Read-through cache in `makeCkanRequest`. Action-based TTL (metadata 300s, datastore 60s, status 3600s). Backend: Cloudflare Cache API on Workers, in-memory LRU on Node. Disable with `CKAN_CACHE_ENABLED=false`. Env vars: `CKAN_CACHE_TTL_DEFAULT`, `CKAN_CACHE_MAX_ENTRIES`, `CKAN_CACHE_MAX_ENTRY_BYTES`.
425
426
-**No authentication**: Uses only public CKAN endpoints
426
427
-**No WebSocket**: MCP over HTTP uses JSON responses (not SSE streaming in Workers)
427
-
-**Domain allowlist**: Optional SSRF hardening via `CKAN_ALLOWED_DOMAINS=domain1.com,domain2.org`. If set, requests to unlisted domains are blocked. Default: no restriction (all public domains allowed). Enforced in `validateServerUrl()`.
428
+
-**SSRF protection** (v0.4.108+): requests are validated against private/internal IP ranges, including hostnames that *resolve* to internal addresses (DNS-based SSRF) — `validateServerUrl()` (string/literal guard) + `isBlockedIp()` + a connection-pinning `lookup` agent on the Node/axios path and `assertHostnameResolvesSafe()` on the fetch/SPARQL path.
429
+
-**Domain allowlist**: `CKAN_ALLOWED_DOMAINS=domain1.com,domain2.org` (comma-separated, default-deny when set). **Mandatory for the HTTP transport**: `TRANSPORT=http` refuses to start without it, unless `CKAN_HTTP_ALLOW_ALL=true` is set (logs a warning). `stdio` is unaffected (no allowlist required). Enforced in `validateServerUrl()` + `assertHttpAllowlistConfigured()`.
428
430
-**Audit logging**: Every `makeCkanRequest` call writes a JSON line to stderr (Node modes only; Workers use `console.log`). Fields: `ts`, `server`, `action`, `cache_hit`, plus relevant query params (`q`, `fq`, `sql` truncated to 200 chars, `id`, `rows`, `limit`).
### Security: HTTP transport requires a domain allowlist
832
+
833
+
The HTTP transport (`TRANSPORT=http`) is network-exposed and **unauthenticated**: any
834
+
client that reaches `POST /mcp` can drive requests through it. To prevent SSRF abuse
835
+
(e.g. a caller pointing `server_url` at internal hosts or cloud metadata), the HTTP
836
+
transport **refuses to start** unless you set a domain allowlist:
837
+
838
+
| Variable | Effect |
839
+
|---|---|
840
+
|`CKAN_ALLOWED_DOMAINS`| Comma-separated allowlist of hostnames the server may query (default-deny). **Required** to start the HTTP transport. Example: `CKAN_ALLOWED_DOMAINS="www.dati.gov.it,dati.comune.messina.it"`|
841
+
|`CKAN_HTTP_ALLOW_ALL=true`| Explicit opt-out: start the HTTP transport **without** an allowlist (logs a security warning). Not recommended when network-exposed. |
842
+
843
+
The default `stdio` transport is unaffected — it stays open so you can query any portal
844
+
locally. Regardless of allowlist, all requests are also validated against private/internal
845
+
IP ranges, including hostnames that *resolve* to internal addresses (DNS-based SSRF, fixed
846
+
in v0.4.108). The official Cloudflare Worker is sandboxed by the platform and does not
847
+
require this setting.
848
+
831
849
### Manual HTTP Testing
832
850
833
851
```bash
834
-
# Start server
835
-
TRANSPORT=http PORT=3001 node dist/index.js
852
+
# Start server (HTTP needs an allowlist — see "Security" above)
0 commit comments