Skip to content

Handle non-IP DNS lookup results#957

Merged
dahlia merged 4 commits into
fedify-dev:2.0-maintenancefrom
SJang1:2.0-maintenance
Jul 19, 2026
Merged

Handle non-IP DNS lookup results#957
dahlia merged 4 commits into
fedify-dev:2.0-maintenancefrom
SJang1:2.0-maintenance

Conversation

@SJang1

@SJang1 SJang1 commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Fixes #956

Ignore non-IP aliases emitted by Node-compatible DNS implementations when a lookup also returns actual IP addresses. Fail closed for alias-only results and continue checking every returned IP.

Assisted-by: Codex:gpt-5

Ignore non-IP aliases emitted by Node-compatible DNS implementations when a lookup also returns actual IP addresses. Fail closed for alias-only results and continue checking every returned IP.

Assisted-by: Codex:gpt-5
Copilot AI review requested due to automatic review settings July 19, 2026 05:52
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 379b1f0a-ad30-4b37-b2b6-20898bebb61b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the validateLookupAddresses function to handle DNS lookup results, specifically ignoring CNAME records from Node.js-compatible runtimes while ensuring actual IP addresses are validated. However, two security concerns were identified: first, a critical SSRF vulnerability where DNS lookup failures are silently caught and ignored, which could allow requests to bypass validation; second, a defense-in-depth improvement to fail closed and throw an error if no valid IP addresses are returned at all.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/vocab-runtime/src/url.ts Outdated
Comment on lines +54 to +57
} catch {
addresses = [];
}
for (const { address, family } of addresses) {
validateLookupAddresses(addresses);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

Security Vulnerability: SSRF Bypass via DNS Lookup Failure

Currently, if the DNS lookup fails (e.g., due to a temporary network issue, DNS timeout, or rate limiting), the catch block silently ignores the error and sets addresses = [].

Since addresses is empty, validateLookupAddresses(addresses) does not throw any error because addresses.length > 0 is false. This allows validatePublicUrl to complete successfully.

When the application subsequently performs the actual HTTP request (e.g., via fetch), the DNS resolution might succeed (either because the temporary issue resolved, or as part of a DNS rebinding attack). If it resolves to a private IP address (e.g., 127.0.0.1), the request will be sent to the private network, completely bypassing the SSRF protection.

Remediation:
Fail closed by throwing a UrlError when the DNS lookup fails.

Suggested change
} catch {
addresses = [];
}
for (const { address, family } of addresses) {
validateLookupAddresses(addresses);
} catch (error) {
throw new UrlError("DNS lookup failed", { cause: error });
}
validateLookupAddresses(addresses);

Comment thread packages/vocab-runtime/src/url.ts Outdated
Comment on lines +80 to +82
if (addresses.length > 0 && ipAddressCount === 0) {
throw new UrlError("DNS lookup did not return an IP address");
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

Security Improvement: Fail Closed on Empty DNS Results

If the DNS lookup succeeds but returns an empty list of addresses, or if an empty list is passed, the function currently returns successfully without validating any IP addresses.

To ensure robust SSRF protection (defense in depth), we should fail closed and throw an error if no valid IP addresses are returned, regardless of whether the input array was empty or not.

Suggested change
if (addresses.length > 0 && ipAddressCount === 0) {
throw new UrlError("DNS lookup did not return an IP address");
}
if (ipAddressCount === 0) {
throw new UrlError("DNS lookup did not return any IP address");
}

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves @fedify/vocab-runtime’s SSRF-oriented URL validation to tolerate Node-compatible DNS implementations that include non-IP alias (e.g., CNAME target hostnames) entries in dns.lookup({ all: true }) results, while still validating every actual IP address and failing closed when only aliases are returned.

Changes:

  • Refactors DNS result checking into validateLookupAddresses() and uses it from validatePublicUrl().
  • Ignores non-IP address entries when at least one real IP is present; rejects alias-only results.
  • Adds unit tests covering mixed alias+IP results and alias-only / unsafe-IP scenarios.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
packages/vocab-runtime/src/url.ts Adds validateLookupAddresses() and switches validatePublicUrl() to validate only actual IP literals while failing closed on alias-only DNS results.
packages/vocab-runtime/src/url.test.ts Adds tests asserting that alias entries are ignored when IPs are present and that alias-only / private-IP results are rejected.

Comment thread packages/vocab-runtime/src/url.ts Outdated
Comment on lines +80 to +82
if (addresses.length > 0 && ipAddressCount === 0) {
throw new UrlError("DNS lookup did not return an IP address");
}
@codecov

codecov Bot commented Jul 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

Files with missing lines Coverage Δ
packages/vocab-runtime/src/url.ts 89.63% <100.00%> (+0.54%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1102bf5ba9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

*
* @internal
*/
export function validateLookupAddresses(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the required changelog entry

Because this commit fixes #956 in @fedify/vocab-runtime, it falls under the Bugfix process in /workspace/fedify/AGENTS.md, which requires updating CHANGES.md with the issue/PR reference and contributor name. I checked the current Version 2.0.23 unreleased section and it remains empty, so this user-facing fix will be omitted from release notes unless the changelog entry is added before merge.

Useful? React with 👍 / 👎.

@SJang1
SJang1 force-pushed the 2.0-maintenance branch from 1102bf5 to be6b35d Compare July 19, 2026 06:18
@dahlia dahlia self-assigned this Jul 19, 2026
@dahlia dahlia added component/vocab Activity Vocabulary related runtime/cfworkers Cloudflare Workers runtime related labels Jul 19, 2026
Comment thread packages/vocab-runtime/src/url.ts Outdated
/**
* Validates the IP addresses returned by `node:dns.lookup()`.
*
* Some Node.js-compatible runtimes include CNAME records in lookup results,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you make it explicit here that this is a workaround for a bug in Cloudflare Workers' node:dns implementation, rather than a general variation among Node-compatible runtimes? Workerd currently maps every record in the DNS-over-HTTPS Answer array—including CNAME records—to LookupAddress, even though Node.js specifies that address is an IPv4 or IPv6 literal.

Please link the upstream Workerd issue once it is filed, and explain why this remains fail-closed: non-IP entries may be ignored only when at least one actual IP address is present, and every returned IP must still be validated. That context will help future readers understand why the spec-violating input is tolerated and when the workaround can be revisited. It would also be helpful to rename the test to mention Cloudflare Workers so the regression's origin is visible there as well.

SJang1 added 2 commits July 19, 2026 16:32
Clarify that non-IP lookup entries are tolerated only for Workerd issue #6886 while retaining fail-closed validation, and identify the regression source in test names.

Assisted-by: Codex:gpt-5
@dahlia
dahlia merged commit 43673cb into fedify-dev:2.0-maintenance Jul 19, 2026
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/vocab Activity Vocabulary related runtime/cfworkers Cloudflare Workers runtime related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cloudflare Workers: document loader rejects public CNAME-backed hosts as private addresses

3 participants