Skip to content

Add a DNS resolution timeout and AbortController to lookup() in src/webhooks/ssrfGuard.ts#552

Merged
Jagadeeshftw merged 2 commits into
Fluxora-Org:mainfrom
attyolu:fix/ssrf-dns-timeout
Jun 29, 2026
Merged

Add a DNS resolution timeout and AbortController to lookup() in src/webhooks/ssrfGuard.ts#552
Jagadeeshftw merged 2 commits into
Fluxora-Org:mainfrom
attyolu:fix/ssrf-dns-timeout

Conversation

@attyolu

@attyolu attyolu commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

##closes #485

# fix(ssrf): add DNS lookup timeout and abort to webhook target validation

## Description
This PR resolves issue #485 by adding a configurable DNS resolution timeout to the SSRF guard `validateWebhookTarget`. 

Currently, the SSRF guard calls `dns.promises.lookup(hostname, { all: false })` without a deadline. If a target resolver is slow, unresponsive, or maliciously configured, it can hang indefinitely, stalling the webhook validation path and blocking caller delivery threads.

## Key Changes

### 1. Configuration
- Added the `WEBHOOK_DNS_TIMEOUT_MS` environment variable (default: `2000` ms) to the environment schema in `src/config/env.ts`.
- Added schema tests in `src/config/env.test.ts` to verify default behavior and ensure configuration bounds are correctly enforced.

### 2. SSRF Guard Logic & Fail-Closed Behavior
- Implemented `lookupWithTimeout` helper function in `src/webhooks/ssrfGuard.ts` which wraps `dns.promises.lookup` using a `Promise.race` timeout and an `AbortController`.
- Pass the `AbortSignal` to Node's `dns.promises.lookup`. If a timeout is hit, the controller signals an abort (cancelling the pending lookup to prevent memory/resource leaks) and rejects the target with a `'DNS_TIMEOUT'` validation error code.
- Added specific error codes (e.g. `'DNS_TIMEOUT'`, `'BLOCKED_ADDRESS'`) to `WebhookTargetValidationError` to easily distinguish DNS timeout events from private address rejections.

### 3. Unit Testing
- Added `tests/webhooks/ssrfGuard.test.ts` asserting:
  - Timeout fail-closed behavior when a DNS lookup hangs.
  - That the controller successfully aborts the signal on timeout to prevent leaking lookups.
  - Fail-closed behavior on lookup errors (e.g. `ENOTFOUND`).
  - Standard public and private IP resolution validations.

### 4. Documentation
- Documented `WEBHOOK_DNS_TIMEOUT_MS` in the configuration tables of `README.md` and `docs/webhooks.md`.
- Documented DNS resolution timeout details and error responses in `docs/webhooks.md`.

## Security Notes
- **Fail-Closed Semantics**: A DNS timeout throws a validation error and rejects the target as unsafe, ensuring we fail-closed rather than allowing delivery.
- **Resource Protection**: The lookup is aborted via Node's native cancellation support (`AbortSignal`) to ensure resources are not leaked during timeouts.

@drips-wave

drips-wave Bot commented Jun 27, 2026

Copy link
Copy Markdown

@attyolu Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Jagadeeshftw Jagadeeshftw merged commit 3c20b0e into Fluxora-Org:main Jun 29, 2026
@Jagadeeshftw

Copy link
Copy Markdown
Contributor

important security hardening - the ssrf guard was calling dns.promises.lookup without any deadline, so a slow or malicious resolver could hang webhook validation indefinitely. the lookupWithTimeout helper uses Promise.race with an AbortController so the lookup is actually cancelled on timeout (not just abandoned), and the fail-closed behavior means a dns timeout rejects the target rather than letting it through. WEBHOOK_DNS_TIMEOUT_MS defaulting to 2000ms is a sensible conservative value.

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.

Add a DNS resolution timeout and AbortController to lookup() in src/webhooks/ssrfGuard.ts

3 participants