CrawlForge v5.0.0: Security, Correctness, MCP Spec CrawlForge MCP Server v5.0.0 addresses a critical SSRF vulnerability that allowed IP-literal URLs to bypass hostname-based guards, potentially exposing loopback, link-local, and cloud metadata endpoints. The update also includes 95 fixes across security, correctness, and MCP spec compliance, raising the Node.js requirement to >=20.16.0. http://2130706433/ is a valid URL. Your browser will happily resolve it to 127.0.0.1 , because the WHATWG URL parser normalizes decimal, hex 0x7f000001 , and octal integer forms into dotted-quad IPv4. Our SSRF guard did not know that. It resolved hostnames through DNS and range-checked the resulting addresses — but Node never routes an IP literal through lookup , so a URL whose host was already an IP sailed straight past the check. Loopback, link-local, cloud metadata: all reachable, in a server whose entire job is fetching URLs a model picked for you. That is one bug out of the seven-phase internal audit that became CrawlForge MCP Server v5.0.0 . The unit suite went from npm audit went from | Phase | Theme | Headline result | |---|---|---| | 0 | Dependency currency | npm audit 16 vulns → 4 moderate, zero code change | | 1 | Critical security | SSRF IP-literal bypass, OAuth token minting, secret leakage, billing | | 2 | Correctness | 52 fixes — including a crawl deep rewrite | | 3 | Leaks and timeouts | 24 fixes — browser contexts, unbounded caches, real deadlines | | 4 | HTTP transport | 19 fixes — multi-session streamable HTTP, working prompts, webhook HMAC | | 5 | Dependency modernization | Node ≥ 20 floor, 0 npm audit vulnerabilities | | 6 | MCP spec adoption | Structured output, async tasks, tool whitelisting, registry server.json | MCP protocol compliance held at 100.0% COMPLIANT, 0 errors at every phase gate. engines.node moved from =18.0.0 to =20.16.0 . Node 18 hit end-of-life in April 2025, and 20.16 is the floor required by pdf-parse 2.4.5 — the maintained ESM rewrite we needed to clear the last audit findings. Our Dockerfile node:20-alpine and CI Node 22 already satisfied it. That is the entire breaking surface. No tool schema, output shape, or credit cost changed , and the tool count stays at 27. node --version must be = 20.16.0 Read this phase if you run any MCP scraping server near a private network. php BEFORE: url - parse - DNS lookup - ipBlocked resolved ? - fetch | +-- IP literal? no lookup happens. guard never runs. request goes out. AFTER: url - parse - ipBlocked literal host ? --------+ - DNS lookup - ipBlocked addrs ? -+- fetch - per-connect check in the undici dispatcher catches every redirect hop too v5.0.0 runs ipBlocked on IP-literal hostnames at pre-flight and wraps the undici dispatcher's buildConnector with a per-connect check, so a redirect hop straight to an internal address is blocked as well. Three more guard fixes landed with it: ::ffff:127.0.0.1 and ::ffff:169.254.169.254 are normalized to their embedded IPv4 before range checks, in both default and strict modes. Kills the DNS-controlled AAAA-record bypass. BLOCKED DOMAINS was dead config.We also wired the guard into five paths that never had it: scrape with actions with a post-navigation page.url re-check that closes the page on a redirect into a blocked range — that was a Playwright internal-network read primitive , map site , process document PDF downloads, webhook delivery and health checks, and deep research webhook notifications. /oauth/authorize now requires proof of the operator's API key before issuing a code, with constant-time digest comparison. The anonymous register → authorize → token flow that minted operator-billed bearer tokens is closed. Secret leakage. Usage telemetry passes tool params through maskSecrets before the payload leaves the process — third-party API keys, auth headers, and webhook signing secrets no longer travel in plaintext. deep research stopped writing LLM API keys to Winston file logs. Billing. A throw from the credit check itself now bills zero ; the error-path half-charge only applies once the handler has actually started. checkCredits distinguishes 401/403 invalid or revoked key from 5xx grace window instead of reporting both as "insufficient credits." If you want the general version of this problem rather than our specific one, we wrote it up separately: SSRF in MCP servers https://www.crawlforge.dev/blog/mcp-server-ssrf-cloud-metadata-security . This is the "passes smoke tests, returns misleading output" class — the one that never shows up as an error in your logs. crawl deep is usable for real crawls again. BFS child pages were awaited from inside an occupied queue slot, so the per-task queue timeout bounded the Promise timed out , and low concurrency settings including concurrency: 1 deadlocked outright. Both fixed. crawl deep 's result-cache key now covers extract content , content length, include/exclude patterns, follow external , respect robots , concurrency , domain filter, and session. map site 's covers search , domain filter, include metadata , and group by path . Previously a cached call could contradict your parameters for a full hour-long TTL. Content-Type header or