Skip to content

feat(security)!: add SSRF protection with safe-by-default DNS policy#17

Merged
chaliy merged 3 commits intomainfrom
claude/fetchkit-security-analysis-OHGfK
Feb 15, 2026
Merged

feat(security)!: add SSRF protection with safe-by-default DNS policy#17
chaliy merged 3 commits intomainfrom
claude/fetchkit-security-analysis-OHGfK

Conversation

@chaliy
Copy link
Contributor

@chaliy chaliy commented Feb 15, 2026

What

Add comprehensive SSRF protection to FetchKit with a resolve-then-check DNS policy that blocks private/reserved IP ranges by default.

Why

FetchKit is designed to be embedded in AI agent platforms (e.g., Everruns) where untrusted user prompts influence which URLs are fetched. Without SSRF protection, agents running in containers/clusters can be tricked into accessing internal resources (cloud metadata, Kubernetes API, internal services).

How

  • DnsPolicy module (crates/fetchkit/src/dns.rs): Resolves hostnames to IPs before connecting, validates against 14 blocked IP ranges, pins validated IP via reqwest::resolve() to prevent DNS rebinding, handles IPv6-mapped-IPv4 via to_canonical()
  • Safe-by-default: DnsPolicy::default() blocks private IPs. Tool::default(), Tool::builder().build(), and fetch() all block by default. Explicit opt-out via block_private_ips(false) for local dev
  • Integrated into both fetchers: DefaultFetcher and GitHubRepoFetcher both validate DNS before connecting
  • Threat model (specs/threat-model.md): 37 threats across 6 categories with stable TM-XXX-NNN IDs, mitigation tracking, and vulnerability summary

Blocked IP ranges

Loopback, private (10/172/192), link-local (169.254 incl. cloud metadata), carrier-grade NAT, documentation, benchmarking, multicast, broadcast, unspecified, IPv6 unique-local/link-local

Risk

  • Medium
  • BREAKING: DnsPolicy::default() now blocks private IPs. Any consumer connecting to private IPs without explicit opt-out will get BlockedUrl errors. This is intentional — safe-by-default.
  • Error message changed from "prefix not allowed" to "not allowed by policy"

Checklist

  • Unit tests passed (70 unit tests including 26 DNS policy tests)
  • Integration tests passed (27 tests, updated for safe-by-default)
  • SSRF security tests passed (19 exploitative tests)
  • Doc tests passed (13 tests)
  • Clippy clean (-D warnings)
  • Formatting clean (cargo fmt)
  • Documentation updated (README, specs/initial.md, specs/fetchers.md, AGENTS.md)
  • Threat model created (specs/threat-model.md)
  • Specs up to date

https://claude.ai/code/session_011H7YzDQ8VXbaNT7nXGQgmE

Add threat model (specs/threat-model.md) with 37 identified threats across
6 categories (SSRF, Network, Input, DoS, Leakage, Conversion) using stable
TM-XXX-NNN IDs.

Implement resolve-then-check in fetchkit core:
- DnsPolicy resolves hostnames to IPs before connecting
- Validates against blocked ranges (private, loopback, link-local,
  metadata, multicast, etc.)
- Pins validated IP via reqwest::resolve() to prevent DNS rebinding
- Handles IPv6-mapped-IPv4 via to_canonical()
- Opt-in via ToolBuilder::block_private_ips(true)

Integrate into both DefaultFetcher and GitHubRepoFetcher.
Add 20 SSRF security tests covering attack vectors from threat model.

https://claude.ai/code/session_011H7YzDQ8VXbaNT7nXGQgmE
BREAKING: DnsPolicy::default() now blocks private/reserved IPs.
Tool::default() and fetch() block loopback, private, link-local,
metadata, multicast, and other reserved IP ranges out of the box.

Callers that need to connect to private IPs (e.g., local dev servers)
must explicitly opt out via:
  Tool::builder().block_private_ips(false).build()

This is a security-first default — better to block by default and
require explicit opt-out than to be permissive and hope callers
remember to enable protection.

https://claude.ai/code/session_011H7YzDQ8VXbaNT7nXGQgmE
- specs/initial.md: add SSRF prevention section, update error messages,
  add block_private_ips to tool builder, add SSRF test requirements
- specs/fetchers.md: add dns_policy to FetchOptions, add SSRF section,
  add dns.rs to module structure
- AGENTS.md: add threat-model.md to available specs list
- README.md: add SSRF protection to features, add Security section
  with usage examples

https://claude.ai/code/session_011H7YzDQ8VXbaNT7nXGQgmE
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@chaliy chaliy merged commit 19d0d4f into main Feb 15, 2026
7 checks passed
@chaliy chaliy deleted the claude/fetchkit-security-analysis-OHGfK branch February 15, 2026 20:43
@chaliy chaliy mentioned this pull request Feb 16, 2026
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants