Skip to content

Commit 96eaf2d

Browse files
authored
fix(security): remove deep-research search/fetch tools (GHSA-j7p8-g5m6-3wv5) (#162)
The openai_deep_research module cached Meta Ads records in a module-level singleton keyed only by "<type>:<id>". Under streamable-http one process serves every caller, and fetch read that cache with no credential resolution and no ownership check, so any caller could read another caller's cached account, campaign, ad, page and business records. Removed rather than scoped: in production every fetch call already failed (70% "Record not found", the rest schema-validation errors from clients treating it as a generic Graph fetch), and the hosted MCP replaces search with its own implementation, so there was no working behaviour to preserve. Regression tests assert neither tool is registered again. Also bumps to 1.0.122 and documents the advisory in SECURITY.md.
1 parent ceee65b commit 96eaf2d

12 files changed

Lines changed: 99 additions & 1443 deletions

‎README.md‎

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -502,14 +502,6 @@ For advanced users who need to self-host, the package can be installed from sour
502502
- `limit`: Maximum number of results to return (default: 25)
503503
- Returns: Location data with key, name, type, and geographic hierarchy information
504504

505-
29. `mcp_meta_ads_search` (Enhanced)
506-
- Generic search across accounts, campaigns, ads, and pages
507-
- Automatically includes page searching when query mentions "page" or "pages"
508-
- Inputs:
509-
- `access_token` (optional): Meta API access token (will use cached token if not provided)
510-
- `query`: Search query string (e.g., "Injury Payouts pages", "active campaigns")
511-
- Returns: List of matching record IDs in ChatGPT-compatible format
512-
513505
## Licensing
514506

515507
Meta Ads MCP is licensed under the [Business Source License 1.1](LICENSE), which means:

‎SECURITY.md‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,44 @@ same-origin page — the escalation target for the XSS above.
180180
- If you completed an OAuth login on a machine where an untrusted page may have
181181
been open, reconnect the Meta account so a fresh token is issued.
182182
- Credited to zx — GitHub [@manus-pi](https://github.com/manus-pi).
183+
184+
### GHSA-j7p8-g5m6-3wv5 — `search`/`fetch` deep-research tools returned one caller's cached records to another
185+
186+
- **Severity:** High (CVSS 3.1 7.5 — `AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N`)
187+
- **Affected versions:** `>= 0.6.0, <= 1.0.121` when run with
188+
`--transport streamable-http` and serving more than one caller from one
189+
process.
190+
- **Fixed in:** `1.0.122`
191+
- **Affected configurations:** Multi-caller deployments only. The default stdio
192+
transport, and any deployment where a process serves a single caller, has no
193+
cross-caller boundary to cross. The hosted MCP at `*.mcp.pipeboard.co`
194+
intercepts `search` and answers it with its own implementation, so the Python
195+
cache was never populated there and `fetch` had nothing to return — measured
196+
over production traffic, every hosted `fetch` call returned "Record not found"
197+
or a schema-validation error.
198+
199+
**What went wrong.** `openai_deep_research.py` cached Meta Ads records in a
200+
module-level `MetaAdsDataManager` singleton, keyed only by `"<type>:<id>"` with
201+
no caller, session or tenant component. `search` populated that cache under the
202+
calling user's credential; `fetch` read it back with no credential resolution
203+
and no ownership check, so any caller who knew (or guessed) a record id could
204+
read another caller's cached ad-account, campaign, ad, page or business records
205+
— including account name, currency, total spend, balance and the raw Graph
206+
objects. `fetch` never called the Graph API, so nothing downstream validated
207+
the reader's token either; the HTTP auth gate gets a request past on any
208+
non-empty bearer token, which is enough for every other tool because Graph
209+
rejects a bad token, but not for a tool that answers from local state.
210+
211+
**Fix.** The `search` and `fetch` tools and the module that backed them were
212+
removed outright. They had no working use left to preserve, so scoping the
213+
cache per caller would have kept the machinery without the benefit.
214+
215+
**Action for operators.**
216+
- Upgrade to `1.0.122` or later.
217+
- If you ran an earlier version as a shared multi-caller service, treat the
218+
record types listed above as potentially disclosed to other callers of that
219+
process.
220+
- Clients that relied on the deep-research `search`/`fetch` pair should use the
221+
typed tools instead: `get_ad_accounts`, `get_campaigns`,
222+
`get_campaign_details`, `get_adsets`, `get_ads`, `get_ad_details`.
223+
- Credited to [@BarakSrour](https://github.com/BarakSrour).

‎meta_ads_mcp/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from meta_ads_mcp.core.server import main
88

9-
__version__ = "1.0.121"
9+
__version__ = "1.0.122"
1010

1111
__all__ = [
1212
'get_ad_accounts',

‎meta_ads_mcp/core/__init__.py‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from .targeting import search_interests, get_interest_suggestions, estimate_audience_size, search_behaviors, search_demographics, search_geo_locations
1515
from . import reports # Import module to register conditional tools
1616
from . import duplication # Import module to register conditional duplication tools
17-
from .openai_deep_research import search, fetch # OpenAI MCP Deep Research tools
1817

1918
__all__ = [
2019
'mcp_server',
@@ -44,6 +43,4 @@
4443
'search_behaviors',
4544
'search_demographics',
4645
'search_geo_locations',
47-
'search', # OpenAI MCP Deep Research search tool
48-
'fetch', # OpenAI MCP Deep Research fetch tool
4946
]

0 commit comments

Comments
 (0)