Please report security issues privately via GitHub: open a draft advisory from the Security tab ("Report a vulnerability"). Do not file public issues for unpatched vulnerabilities.
GHSA-45gf-fjxp-cjpq — Server-Side Request Forgery (SSRF) in upload_ad_image via unrestricted image_url fetch
- Severity: High (CVSS 3.1 8.3 —
AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:L) - Affected versions:
<= 1.0.114when run with--transport streamable-http. - Fixed in:
1.0.115 - Affected configurations: Self-hosted deployments that expose the
streamable-HTTP port on a reachable network interface, especially when
co-located with internal services or a cloud metadata endpoint. The hosted
MCP at
*.mcp.pipeboard.cobinds the Python process to localhost behind an authenticating proxy, so it was not reachable by unauthenticated callers; the fix is still applied there as defense-in-depth.
What went wrong. upload_ad_image (and the image-viewing tools) passed a
caller-supplied URL straight to an HTTP client (download_image /
try_multiple_download_methods) with follow_redirects=True and no scheme,
host, or IP validation. A caller could supply http://127.0.0.1/..., an RFC
1918 address, or http://169.254.169.254/ (cloud instance metadata) and make
the server issue outbound requests to those targets. On the streamable-HTTP
transport the image fetch ran before Meta credential validation, so any
non-empty bearer token reached the sink.
Fix.
- A new
validate_public_url()guard restricts fetches tohttp/httpsURLs whose host resolves only to public addresses. Private, loopback, link-local (incl.169.254.169.254), reserved, multicast, and unspecified addresses are rejected; IPv4-mapped IPv6 addresses are unwrapped first. - An httpx request event hook re-validates every redirect hop, so a public URL cannot redirect into a private/internal address.
Action for operators.
- Upgrade to
1.0.115or later. - If you exposed an earlier version on a reachable interface, review outbound request and access logs for unexpected internal fetches, and ensure the cloud metadata service is hardened (e.g. IMDSv2 / metadata not reachable from the app process).
- Severity: Critical (CVSS 3.1 9.1 —
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N) - Affected versions:
<= 1.0.108when run with--transport streamable-httpand aMETA_ACCESS_TOKENenvironment variable. - Fixed in:
1.0.109 - Affected configurations: Self-hosted deployments that expose the
streamable-HTTP port on a reachable network interface. The hosted MCP at
*.mcp.pipeboard.cowas not affected — it sits behind an authenticating proxy and the Python process is bound to localhost.
What went wrong. AuthInjectionMiddleware.dispatch() logged a warning when
a request arrived with no Authorization: Bearer / X-PIPEBOARD-API-TOKEN
header and then forwarded the request to the tool handler anyway. Tool handlers
fall back to META_ACCESS_TOKEN when no per-request token is set, so any
network-reachable caller could invoke any MCP tool as the operator. When the
downstream Graph API call returned a 4xx, make_api_request() serialized
e.request.url — including access_token as a query parameter — verbatim into
the JSON-RPC error payload, exposing the long-lived operator credential.
Fix.
AuthInjectionMiddlewarenow returns401 UnauthorizedwithWWW-Authenticate: Bearerwhen neither token header is present.make_api_request()redactsaccess_tokenandappsecret_prooffrom any URLs returned in error payloads (_redact_urlhelper).
Action for operators.
- Upgrade to
1.0.109or later. - HTTP clients must send
Authorization: Bearer <meta-access-token>(or the legacyX-PIPEBOARD-API-TOKENheader) on every request. TheMETA_ACCESS_TOKENenv var is no longer used as an implicit fallback for HTTP transport. - If you exposed an earlier version to an untrusted network, rotate the Meta
access token (
https://developers.facebook.com/tools/debug/accesstoken/) and review Graph API access logs for unexpected calls.
- Severity: High (CVSS 3.1 7.4 —
AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N) - Affected versions:
<= 1.0.113when run with--transport streamable-httpand aMETA_ACCESS_TOKENenvironment variable. - Fixed in:
1.0.115 - Affected configurations: Self-hosted deployments that expose the
streamable-HTTP port on a reachable network interface with
META_ACCESS_TOKENset. The hosted MCP at*.mcp.pipeboard.cowas not affected — it does not setMETA_ACCESS_TOKENand binds the Python process to localhost behind an authenticating proxy.
What went wrong. This is a follow-up to GHSA-9gw6-46qc-99vr. After that fix,
AuthInjectionMiddleware rejected a request only when both a primary token and
a supplementary token were absent. X-Pipeboard-Token is a supplementary service
token (used only for the duplication callback) and is read into the supplementary
slot, so a request carrying X-Pipeboard-Token alone — with any arbitrary value —
satisfied the guard and passed through. No request auth context was set, so tool
handlers fell back to the operator's META_ACCESS_TOKEN, letting a
network-reachable caller act as the operator.
Fix. AuthInjectionMiddleware now requires a primary access-token credential
(Authorization: Bearer, X-META-ACCESS-TOKEN, or X-PIPEBOARD-API-TOKEN) and
returns 401 Unauthorized otherwise. X-Pipeboard-Token is treated as a
supplementary token only and cannot, on its own, admit a request.
Action for operators.
- Upgrade to
1.0.115or later. - If you exposed an earlier version on a reachable network with
META_ACCESS_TOKENset, rotate the Meta access token (https://developers.facebook.com/tools/debug/accesstoken/) and review Graph API access logs for unexpected calls.