T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/firecrawl-wrap.sh:21
- Finding
- Unrestricted HTTP(S) Targets Permit Server-Side Request Forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/firecrawl-wrap.sh:21-33`; related fetch instructions in `SKILL.md:97-114` **Vulnerability Type**: Server-Side Request Forgery caused by insufficient URL validation **Risk Level**: High ### Complete Code Snippet ```bash # Validate URL format (must start with http:// or https://) if [[ ! "$URL" =~ ^https?:// ]]; then echo '{"error": "Invalid URL: must start with http:// or https://"}' >&2 exit 1 fi # Run Firecrawl only when a bounded timeout command is available. TIMEOUT_BIN="$(command -v timeout 2>/dev/null || command -v gtimeout 2>/dev/null || true)" if [[ -z "$TIMEOUT_BIN" ]]; then echo "FIRECRAWL_UNAVAILABLE" exit 0 fi result=$("$TIMEOUT_BIN" 30 firecrawl scrape -- "$URL" --format markdown 2>/dev/null || echo "") ``` The related agent instructions apply similarly unrestricted URLs to all fetch tiers: ```text **Tier 1 — web_fetch (fast):** Call web_fetch(url) If content length >= 200 chars → accept, trim to max_chars_per_source **Tier 2 — Firecrawl (deep/JS):** If Tier 1 fails or returns < 200 chars: Run: bash "{baseDir}/scripts/firecrawl-wrap.sh" <url> <max_chars> If output != "FIRECRAWL_UNAVAILABLE" and != "FIRECRAWL_EMPTY" → accept **Tier 3 — Browser (last resort):** If Tier 2 fails: Call browser(action="open", url=url) Call browser(action="snapshot") ``` ### Technical Analysis The Firecrawl wrapper validates only whether a URL begins with `http://` or `https://`. It does not parse and validate the destination hostname or resolved address. Consequently, it does not reject: - IPv4 or IPv6 loopback addresses. - RFC 1918 private network addresses. - Link-local addresses. - Cloud metadata service addresses. - Reserved, multicast, or otherwise non-public destinations. - Public hostnames that resolve to non-public addresses. - Redirects from an initially public URL to a prohibited destination. The same untrusted URL is used by `web_fetch`, Firecrawl, and browser-based fallbac ...[truncated 1658 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse URLs with a standards-compliant URL parser instead of a shell regular expression. 2. Permit only `http` and `https`, and reject embedded credentials, malformed hosts, ambiguous numeric addresses, and unexpected ports. 3. Resolve every hostname before connecting and reject all loopback, private, link-local, multicast, reserved, and metadata-service address ranges for both IPv4 and IPv6. 4. Prevent DNS rebinding by connecting only to the validated resolved address while preserving the intended HTTP host and TLS server name. 5. Disable redirects or validate the destination of every redirect using the same rules. 6. Apply the same destination policy consistently to `web_fetch`, Firecrawl, and browser fetches. Validation solely inside the Firecrawl wrapper does not protect the other tiers. 7. Prefer an egress proxy that enforces public-Internet-only access independently of model output and script behavior. 8. Where practical, allow only URLs returned directly by the trusted search provider and reject URLs added or modified by the LLM. 9. Add tests covering IPv4, IPv6, alternative address encodings, user-info syntax, redirects, DNS rebinding, and cloud metadata endpoints. ]]>
