
API Development
Outbound Webhook SSRF: QA Tests for Agent-Built Apps
Enforce outbound webhook SSRF controls: block non-public IPs, validate redirects/DNS, require CI tests and human approval.
6 min read

Outbound Webhook SSRF: QA Tests for Agent-Built Apps
I treat webhook SSRF tests as passed only when logs show zero prohibited connection attempts. A delivered webhook - or an error response - doesn’t prove the sender is safe.
Before shipping, I check that the app:
- Blocks non-public IPv4 and IPv6 destinations, including disguised address formats.
- Applies the same rules during registration, delivery, retries, and fallbacks.
- Checks every redirect and connects only to validated IP addresses, even when DNS answers change.
- Runs required CI tests with connection logs, plus Builder, Reviewer, and QA agent checks.
I run these tests in an isolated network with controlled targets. My release rule is simple: agents prepare the results; people approve them. <u>Explicit human approval is required before merge and release.</u>

SSRF: The call is coming from inside your network
sbb-itb-622dc24
Checklist: Check Destination Addresses and URL Parsing
Apply the same destination policy across registration, delivery, retry, and fallback paths. Parse once, then classify the final connection target. Approval at registration must not bypass delivery-time checks.
For every test, save the input, expected decision, actual result, and connection logs. Tag each record with its execution path.
Block Non-Public IPv4 and IPv6 Destinations
Check that loopback, private, link-local, metadata-service, and other non-public IPv4/IPv6 targets - including IPv4-mapped IPv6 - are blocked before reaching a real service. Every rejected case must show no connection attempt.
| Destination category | Controlled input | Expected rejection | Connection evidence |
|---|---|---|---|
| Loopback | IPv4 and IPv6 loopback addresses | Block loopback destinations | No connection attempt |
| Private and non-public | Private IPv4 and IPv6 destinations | Block non-public addresses | No connection attempt |
| Link-local and metadata-service | Link-local and metadata-service destinations | Block these destinations | No connection attempt |
| IPv4-mapped IPv6 | IPv4-mapped IPv6 addresses | Block prohibited underlying addresses | No connection attempt |
| DNS-backed destinations | Hostnames resolving to blocked fixtures | Block the resolved address | No connection attempt to the resolved address |
Keep the blocked set explicit and test it. A hostname’s appearance alone is not enough to decide whether its destination is allowed.
Test Unusual IP Formats and URL Syntax
Set strict rules for input characters, formats, and ranges. Test edge cases across registration, delivery, retry, and fallback paths to verify that each path resolves the input to the same destination. Reject malformed or ambiguous inputs instead of repairing them into a different destination.
| Input or syntax | Parser behavior to check | Expected result | Connection evidence |
|---|---|---|---|
| Unusual IP formats | Whether all paths resolve to the same destination | Reject ambiguous forms | Parsed host, normalized form, zero attempts |
| IPv4-mapped IPv6 forms | Whether equivalent forms receive the same classification | Block prohibited underlying addresses | Normalized address, decision, zero attempts |
| Unusual characters or invalid inputs | Whether parsing changes the destination meaning | Reject malformed input | Escaped input, parser decision, zero attempts |
| Path-specific parsing differences | Whether all paths agree | Reject any disagreement | Parsed host, expected decision, actual result, zero attempts |
Repeat the same tests across every path. Any disagreement is a failed test, even when no connection is attempted.
Checklist: Check Redirects and DNS Changes
After parsing checks, verify where the socket actually connects. Use isolated redirect endpoints and controlled DNS fixtures.
Block Unsafe Redirects at Every Hop
Disable automatic redirects unless they're required. If they're enabled, resolve each Location against the current URL and validate the next destination before connecting.
Test direct, multi-hop, relative, and cross-host redirects to unsafe internal destinations. Set a finite hop limit, and verify that redirect loops and chains exceeding that limit stop.
Stop the delivery path immediately if any hop is blocked.
Connect Only to Validated DNS Addresses
Change controlled A, AAAA, and CNAME answers between validation and connection. Test mixed public/private answers, IPv4/IPv6 fallback, retries, and DNS resolution by a proxy. The client must connect only to the validated public IP.
Bind each connection to that validated public IP, or validate within the connection path without a second unchecked lookup. Preserve the original hostname for TLS hostname verification.
Compare DNS answers, policy decisions, and actual socket destinations. Collect the same evidence when a proxy resolves DNS. A passing result must show that no blocked address received a connection attempt.
Checklist: Require CI Checks and Agent Review
Require Security Tests Before Merge
Once the outbound-webhook SSRF controls pass QA, add the same checks to CI.
Make the SSRF matrix a required pull-request check. Include parser and address-classification unit tests; integration tests for blocked destinations, redirects, and DNS changes; and regression tests for every fixed bypass. Fail the check if any required case fails, gets skipped, or lacks connection evidence.
Require instrumentation that proves prohibited connections were never attempted. An HTTP error doesn't count as a pass: a firewall may have blocked a request the app should have rejected. Run policy tests in the isolated fixture network, and test egress controls separately.
Protect branches with required reviews, passing checks, and code-owner approval for security-sensitive paths. CI enforces the matrix; assigned owners keep those checks honest.
Assign Builder, Reviewer, and QA Duties
- The Builder agent implements the policy and tests.
- The QA agent runs the tests and records results.
- The Reviewer agent inspects validation call sites and destination-binding logic, rejects tests that rely on errors alone, and sends unresolved risks or security exceptions to the human technical lead for approval.
Conclusion: Require Human Approval Before Release
After QA, human review is the release gate.
Review Test Results and Production Settings
Review the destination policy ADR, integration results, and staging logs against one requirement: validated destinations only.
Match CI results to connection logs. Allowed connections must use validated addresses, and blocked tests must show zero prohibited attempts. Check egress-control results separately.
Compare staging evidence and infrastructure changes with production firewall and egress settings. Before release, retest any change that could affect destination handling.
Attach Reviewer and QA reports to the release record. Reject any exception that leaves access to prohibited destinations unresolved.
Require explicit human approval before merge and release. Agents can prepare the evidence, but only people approve security decisions.
FAQs
How can I prove my webhook sender never attempted a blocked connection?
Use detailed logging and audit trails in CI/CD and at runtime. Record the destination URL, IP address, and timestamp for every outgoing request. Store these logs securely to prevent changes.
Automate tests to verify that blocklists work as intended. Run regular penetration tests and compliance checks, and audit logs against your restrictions. Require human review of logging configurations and audit logs before approving deployment.
How do I prevent DNS rebinding without breaking TLS verification?
Before sending an outbound request, check the destination hostname in the Host header against a strict allowlist. Resolve the hostname, verify that the resulting IP address is on your IP allowlist, and connect to that validated IP.
During the TLS handshake, verify that the certificate matches the expected hostname, not the resolved IP address.
Which webhook changes should trigger fresh SSRF tests?
Run new Server-Side Request Forgery (SSRF) tests whenever webhook configurations, destination URLs, or internal routing logic change. Cover changes involving private IP destinations, DNS resolution paths, URL redirects, or unusual IP address formats.
For agent-built apps, add these tests to the CI/CD pipeline. Require technical lead review and human approval before merging code or deploying to production.
More from the studio


