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
{{ message }}
Repository navigation
Commit 16b6862
Browse filesBrowse the repository at this point in the historyBrowse files
security: port the 3 reachable upstream fixes, not the other 48 commits (#5)
The weekly upstream-diff sweep reported this fork 51 commits behind
pipeboard-co/meta-ads-mcp. The divergence is deliberate (v1.2.0 excised
pipeboard.co cloud-auth, v1.1.0 made writes opt-in, plus the OAuth
authorization-code migration, the resilience layer and the test rebuild), so
merging upstream means reconciling against code we removed on purpose. Most
of the 51 are Meta-API tool-shape work and readme churn.
Reachability was checked against how we actually launch: run-mcp.sh ->
`python -m meta_ads_mcp`, stdio only, never --transport streamable-http.
Three of the 51 are reachable; the rest are not.
1. 555ae48 (pipeboard-co#145) — token leak. make_api_request took params by reference and
then set request_params["access_token"], so the caller's own dict came back
carrying the live Meta token. core/ads.py echoes that same dict back to the
user in two places: "attempted_updates" on Meta error subcode 1815573 and
"update_data_sent" from the exception handler below it. Hand-ported as a
one-line shallow copy (upstream's patch no longer applies — our api.py has
diverged around it). Wire format unchanged.
2. 7d99263 (pipeboard-co#138, GHSA-45gf-fjxp-cjpq) — SSRF. download_image and
try_multiple_download_methods fetched a caller-supplied URL server-side with
redirects and no host/IP validation, so the server could reach loopback,
private-network and cloud-metadata addresses. Upstream's core/utils.py hunk
applied cleanly; its tests/test_ssrf_url_validation.py came with it.
Skipped its version-bump files.
3. 93083c6 (pipeboard-co#136, CVE-2026-48710) — starlette floor raised to >=1.0.1 in
pyproject.toml and requirements.txt; venv resolved 1.0.0 -> 1.6.0. mcp
stays pinned at 1.12.2 (upstream's commit also bumps mcp to 1.23.0; not
taken). Hygiene only — starlette is exercised only by the HTTP transport.
Every image-fetch codepath is covered, verified by reading call sites rather
than trusting the new tests: ads.py:792, :848, :1082 (download_image) and
:1265 (try_multiple_download_methods) are the only outbound fetches, and all
three download_image callers already return a clean error on falsy bytes, so
a blocked URL surfaces as an error instead of a crash.
Severity qualifier: update_ad_creative and upload_ad_image are both in
write_gate.WRITE_TOOLS and run-mcp.sh does not set META_ADS_MCP_WRITE, so
both are hidden from ListTools and refused at call time in most contexts.
They are live in exactly one scope — ~/.claude.json sets
META_ADS_MCP_WRITE=true for the clients/neon-one project.
Knowingly NOT ported, so the gap is documented rather than implied:
ef2592d (GHSA-8353-5qhw-8hfw), 14d7371 (GHSA-9gw6-46qc-99vr) and 95e8527 all
harden the HTTP auth middleware. Unreachable while we run stdio only, but
--transport streamable-http still exists at core/server.py:213, so that is a
config fact, not a code guarantee. cbe4c63 (drop PIPEBOARD_API_TOKEN auth) we
already did independently in v1.2.0.
Tests:
RED tests/test_api_token_leak.py::TestCallerParamsNotMutated 3 failed, 5 passed
GREEN 8 passed
RED tests/test_ssrf_url_validation.py ImportError (no validate_public_url)
GREEN 33 passed
Full suite: 532 passed, 2 failed — both pre-existing at origin/main
(verified in a detached worktree at 660ff33): test_logging_conversion's
server-init logging test, and test_write_gate's drift alarm, which reports
compute_image_crops / get_ad_video / get_creative_details registered without
a WRITE_TOOLS or READ_TOOLS classification. All three are reads, so the gate
is not leaking writes. Untouched here — separate issue.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
0 commit comments