Skip to content

fix(tools): pin resolved IP in DB connectors to prevent DNS-rebinding SSRF#4725

Merged
waleedlatif1 merged 6 commits into
stagingfrom
waleedlatif1/fix-dns-rebind-ssrf
May 22, 2026
Merged

fix(tools): pin resolved IP in DB connectors to prevent DNS-rebinding SSRF#4725
waleedlatif1 merged 6 commits into
stagingfrom
waleedlatif1/fix-dns-rebind-ssrf

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

Summary

  • validateDatabaseHost resolved an IP that was then discarded; the drivers re-resolved the hostname at connect time, leaving a DNS-rebinding TOCTOU window
  • MongoDB: pass resolved IP via the MongoClient lookup option
  • MySQL: pin the TCP socket via the stream factory; keep config.host for TLS servername
  • PostgreSQL: connect to resolved IP; pass ssl as object with servername so SNI works against the pinned IP
  • Redis: parse the URL explicitly and pass options-only (URL+options doesn't override host due to ioredis's lodash.defaults); set tls.servername for rediss://
  • Neo4j: pin IP only for plain bolt://; leave bolt+s/neo4j+s unchanged so Aura cert validation keeps working (driver hardcodes servername with no override)

Type of Change

  • Bug fix (security)

Testing

Tested manually. Type-check, biome lint, and bun run check:api-validation all pass. Driver source code reviewed to verify each pinning mechanism reaches the underlying net.connect/tls.connect call.

Pre-merge verification still needed against live managed providers (Atlas, RDS, Heroku PG, ElastiCache TLS, Aura).

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

… SSRF

`validateDatabaseHost` resolved an IP that was then discarded — drivers re-resolved
the hostname at connect time, enabling DNS-rebinding TOCTOU.

- mongodb: pass resolved IP via MongoClient `lookup` option
- mysql: pin TCP socket via `stream` factory; keep hostname for TLS servername
- postgresql: connect to resolved IP; pass `ssl` object with `servername` for SNI
- redis: parse URL explicitly and pass options-only (URL+options breaks override
  due to ioredis's lodash.defaults); pin host and set `tls.servername` for rediss
- neo4j: pin IP for plain `bolt://`; leave `bolt+s`/`neo4j+s` unchanged to keep
  Aura cert validation working (driver hardcodes servername with no override)
@vercel
Copy link
Copy Markdown

vercel Bot commented May 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped May 22, 2026 5:21pm

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 22, 2026

PR Summary

Medium Risk
Moderate risk because it changes low-level connection behavior across multiple database drivers (MongoDB/MySQL/Postgres/Redis/Neo4j), which could break connectivity or TLS/SNI expectations in some environments, but the change is localized and security-motivated.

Overview
Closes a DNS-rebinding/TOCTOU gap in the database tools by pinning connections to the IP resolved during validateDatabaseHost instead of allowing drivers to re-resolve hostnames at connect time.

MongoDB now uses a custom lookup (createPinnedLookup) to force the resolved address; MySQL pins the TCP socket via a stream factory; Postgres connects to the resolved IP while preserving SNI via ssl.servername; Redis now parses the URL and instantiates ioredis with explicit host/port/credentials/db (including TLS servername) so the pinned host actually takes effect; Neo4j pins only for non-+s protocols (plain bolt://) to avoid breaking managed TLS behavior.

Reviewed by Cursor Bugbot for commit 32c4b1a. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 22, 2026

Greptile Summary

This PR closes the DNS-rebinding TOCTOU window across all five database connectors by ensuring the IP resolved during the SSRF validation phase is the same IP used for the actual TCP connection — each driver's pinning mechanism is chosen to be compatible with its TLS SNI requirements.

  • MongoDB: lookup option intercepts the driver's internal DNS calls and always returns the pre-resolved IP; the URI keeps config.host so TLS SNI continues to work.
  • MySQL: a stream factory opens the TCP socket directly to the resolved IP, while config.host stays in connectionConfig for mysql2 to use as the TLS servername during the protocol-level SSL upgrade.
  • PostgreSQL: host is overridden to the resolved IP and an ssl object with servername: config.host preserves SNI; preferred mode intentionally skips IP pinning since TLS is optional there.
  • Redis: the URL is parsed manually into discrete ioredis options to avoid the lodash.defaults merge bug that prevented host from being overridden; tls.servername is set to the original hostname for rediss://.
  • Neo4j: IP pinning is applied only for plain bolt://; bolt+s/neo4j+s connections are left unchanged because the driver hardcodes servername from the URI host with no public override, which would break Aura certificate validation.

Confidence Score: 5/5

Safe to merge; each connector's pinning mechanism correctly preserves TLS SNI semantics and the overall security posture is meaningfully improved.

All five connectors correctly thread the resolved IP into the underlying TCP connection while keeping the original hostname available for TLS SNI. The one concrete finding (Redis db-index validation accepting negative integers) is a minor UX issue that surfaces as a Redis-level error rather than a security or data-integrity problem. No regressions in SSL behaviour were introduced.

apps/sim/app/api/tools/redis/execute/route.ts — the db-index path parsing accepts negative numbers; all other files look correct.

Important Files Changed

Filename Overview
apps/sim/app/api/tools/mongodb/utils.ts Adds lookup: createPinnedLookup(...) to MongoClient options to intercept DNS resolution at the driver level; URI still uses config.host so TLS SNI is preserved correctly.
apps/sim/app/api/tools/mysql/utils.ts Introduces a stream factory that opens the TCP socket directly to the resolved IP; original hostname is kept in connectionConfig.host so mysql2 uses it for TLS SNI during the protocol-level SSL upgrade.
apps/sim/app/api/tools/postgresql/utils.ts Pins connection host to resolved IP for all SSL modes except preferred; passes servername: config.host in the ssl object so SNI still uses the original hostname when connecting to a pinned IP.
apps/sim/app/api/tools/redis/execute/route.ts Replaces URL+options construction with explicit option fields to avoid ioredis lodash.defaults merge issue; db path parsing uses parseInt strict-equality check but does not reject negative indices.
apps/sim/app/api/tools/neo4j/utils.ts Pins IP in the Bolt URI for plain bolt:// connections; deliberately skips pinning for bolt+s/neo4j+s because neo4j-driver hardcodes TLS servername from the URI host with no public override — documented trade-off.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Validator as validateDatabaseHost
    participant DNS
    participant Connector as DB Connector
    participant DB as Database Server

    Client->>Validator: host string
    Validator->>DNS: dns.lookup(host)
    DNS-->>Validator: resolvedIP
    Validator->>Validator: isPrivateOrReservedIP check
    Validator-->>Client: resolvedIP

    rect rgb(200, 230, 255)
        Note over Connector,DB: MongoDB - lookup option
        Client->>Connector: createMongoDBConnection
        Connector->>DB: MongoClient with lookup pinned to resolvedIP
    end

    rect rgb(220, 245, 200)
        Note over Connector,DB: MySQL - stream factory
        Client->>Connector: createMySQLConnection
        Connector->>DB: net.connect to resolvedIP then TLS SNI via config.host
    end

    rect rgb(255, 235, 200)
        Note over Connector,DB: PostgreSQL - host override plus servername
        Client->>Connector: createPostgresConnection
        Connector->>DB: postgres host set to resolvedIP with ssl servername config.host
    end

    rect rgb(240, 220, 255)
        Note over Connector,DB: Redis - explicit options object
        Client->>Connector: Redis POST route
        Connector->>DB: new Redis with host resolvedIP and tls servername hostname
    end

    rect rgb(255, 215, 215)
        Note over Connector,DB: Neo4j - URI host replacement
        Client->>Connector: createNeo4jDriver
        alt bolt plain
            Connector->>DB: bolt://resolvedIP:port
        else bolt+s or neo4j+s TLS
            Connector->>DB: bolt+s://config.host:port no IP pinning
        end
    end
Loading

Reviews (4): Last reviewed commit: "fix(tools): reject non-numeric Redis URL..." | Re-trigger Greptile

Comment thread apps/sim/app/api/tools/neo4j/utils.ts
Comment thread apps/sim/app/api/tools/mysql/utils.ts Outdated
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/app/api/tools/postgresql/utils.ts Outdated
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/app/api/tools/postgresql/utils.ts Outdated
Comment thread apps/sim/app/api/tools/redis/execute/route.ts Outdated
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 32c4b1a. Configure here.

@waleedlatif1 waleedlatif1 merged commit afcbcf2 into staging May 22, 2026
14 checks passed
@waleedlatif1 waleedlatif1 deleted the waleedlatif1/fix-dns-rebind-ssrf branch May 22, 2026 17:33
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.

1 participant