Skip to content

fix(project): expand isSafeUri to block RFC-1918 and cloud metadata IP ranges#256

Merged
yash-pouranik merged 5 commits into
geturbackend:mainfrom
anshul23102:fix/issue-245-ssrf-safe-uri
Jun 5, 2026
Merged

fix(project): expand isSafeUri to block RFC-1918 and cloud metadata IP ranges#256
yash-pouranik merged 5 commits into
geturbackend:mainfrom
anshul23102:fix/issue-245-ssrf-safe-uri

Conversation

@anshul23102
Copy link
Copy Markdown
Contributor

@anshul23102 anshul23102 commented Jun 2, 2026

Pull Request Description

Fixes #245

The previous isSafeUri implementation checked only four literal values (localhost, 127.0.0.1, 0.0.0.0, ::1). Any RFC-1918 private address (e.g. 10.0.0.1, 172.20.0.1, 192.168.1.100) or the cloud instance metadata endpoint (169.254.169.254) passed the check unchallenged, enabling SSRF: an attacker could point their external MongoDB URI at an internal service or the cloud metadata API and have the dashboard-api make outbound connections on their behalf.

Root Cause

isSafeUri compared only against a hard-coded list of four specific strings. IP address ranges were not validated, so any numeric host that was not exactly one of those four values was treated as safe.

Solution

  • Adds ipv4ToInt() to convert a dotted-decimal IPv4 string to a 32-bit integer for range comparison.
  • Adds isRestrictedIPv4() that covers all blocked ranges:
    • Loopback 127.0.0.0/8
    • RFC-1918: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
    • Link-local / cloud metadata: 169.254.0.0/16 (covers 169.254.169.254)
    • Unspecified: 0.0.0.0/8
  • Rewrites isSafeUri to route numeric IPv4 hosts through isRestrictedIPv4, keep a hostname denylist for localhost and metadata.google.internal, and add an explicit IPv6 loopback check via Node's net module.

Changes Made

apps/dashboard-api/src/controllers/project.controller.js

  • Added const net = require("net") import.
  • Added ipv4ToInt(ip) helper function.
  • Added isRestrictedIPv4(ip) function covering all restricted IPv4 ranges.
  • Rewrote isSafeUri(uri) to use range-based validation instead of exact-string matching.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

Testing & Validation

Backend Verification

  • Verified API endpoints using Postman.
  • Tested the following cases manually:
Input URI Expected Result
mongodb://localhost/db Blocked Blocked
mongodb://127.0.0.1/db Blocked Blocked
mongodb://127.100.0.1/db Blocked Blocked
mongodb://10.0.0.1/db Blocked Blocked
mongodb://172.16.0.1/db Blocked Blocked
mongodb://172.31.255.255/db Blocked Blocked
mongodb://192.168.1.100/db Blocked Blocked
mongodb://169.254.169.254/db Blocked Blocked
mongodb://metadata.google.internal/db Blocked Blocked
mongodb+srv://cluster.mongodb.net/db Allowed Allowed
mongodb://203.0.113.5/db Allowed Allowed

Screenshots / Recordings

Not applicable. This is a server-side security fix with no visual change.

Checklist

  • My code follows the code style of this project.
  • I have performed a self-review of my code.
  • My changes generate no new warnings or errors.
  • No em dashes or double hyphens in text or comments.
  • No AI/Claude mentions anywhere.

GSSoC Label Request

Maintainer, could you please add the appropriate GSSoC label to this PR? This helps with contribution tracking and scoring under GSSoC '26. Thank you.


Built with for urBackend.

Summary by CodeRabbit

  • Bug Fixes
    • Strengthened validation for external database URIs in configuration: async safety checks (including DNS resolution) now block URIs that resolve to internal hostnames or restricted IP ranges (loopback, RFC1918/private, link-local/metadata, unspecified, and reserved IPv6 ranges), preventing inadvertent connections to internal network targets.

…P ranges

The previous implementation checked only four literal values (localhost,
127.0.0.1, 0.0.0.0, ::1). An attacker could supply a MongoDB URI whose
host is any RFC-1918 private address (e.g. 10.0.0.1, 172.16.0.1,
192.168.1.1) or the cloud instance metadata endpoint (169.254.169.254)
and bypass the check entirely, enabling SSRF against internal services.

Changes:
- Add net module import for IP validation.
- Extract ipv4ToInt() helper that converts a dotted-decimal string to a
  32-bit unsigned integer for range comparisons.
- Add isRestrictedIPv4() that checks all restricted IPv4 ranges:
    - Loopback:       127.0.0.0/8
    - RFC-1918:       10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
    - Link-local /
      cloud metadata: 169.254.0.0/16  (covers 169.254.169.254)
    - Unspecified:    0.0.0.0/8
- Rewrite isSafeUri to call net.isIPv4() and route numeric hosts through
  isRestrictedIPv4(), keeping the hostname denylist for localhost and
  metadata.google.internal, and adding an IPv6 loopback check.

Closes geturbackend#245
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

Warning

Review limit reached

@anshul23102, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 52 minutes and 2 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4efb625f-9e90-4a58-ad77-3e1bf1f642eb

📥 Commits

Reviewing files that changed from the base of the PR and between b3a792e and 48e5ab1.

📒 Files selected for processing (1)
  • apps/dashboard-api/src/controllers/project.controller.js
📝 Walkthrough

Walkthrough

Rewrites URI safety checks in project.controller.js: imports Node net, implements an async isSafeUri that blocks internal hostnames and rejects URIs whose literal or DNS-resolved IPs fall into restricted IPv4/IPv6 ranges; updateExternalConfig now awaits this check before accepting dbUri.

Changes

SSRF Prevention Through URI Safety Checks

Layer / File(s) Summary
Import and async URI validation
apps/dashboard-api/src/controllers/project.controller.js
Adds Node net import and replaces the synchronous isSafeUri with an async implementation that blocks specific internal hostnames, classifies restricted IPv4/IPv6 ranges (loopback, RFC1918/private ranges, link-local/metadata, unspecified, and IPv6 restricted ranges), validates literal IP hosts, and for hostnames resolves A/AAAA records and rejects the URI if any resolved address is restricted. The updateExternalConfig handler now awaits isSafeUri(dbUri) before proceeding.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🐰 I checked each hostname, byte by byte with care,
Resolved A and AAAA so no secrets leak there—
Loopback, private, link-local all barred from the trail,
A careful hop through ranges, so internal routes fail. 🥕🔒

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: expanding isSafeUri to block RFC-1918 and cloud metadata IP ranges, which is the core objective of the changeset.
Linked Issues check ✅ Passed The PR comprehensively addresses all requirements from issue #245: RFC-1918 range blocking, cloud metadata IP blocking, IPv6 protections, DNS resolution checks, and SSRF validation.
Out of Scope Changes check ✅ Passed All changes are within scope: they modify only the isSafeUri function and related IP validation helpers to address SSRF vulnerabilities specified in issue #245.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/dashboard-api/src/controllers/project.controller.js`:
- Around line 517-526: isSafeUri currently only blocks literal IPv4/IPv6
loopback and a couple hostnames, so resolve the hostname portion of the provided
dbUri (use DNS lookup/resolve via node's dns.promises) and reject the URI if any
resolved address falls into restricted ranges (RFC1918 private IPv4, link-local
169.254.0.0/16, 127/8, metadata IPs, and IPv6 equivalents including fc00::/7,
fe80::/10, ::1, etc.); make isSafeUri async (or add an async helper
isSafeUriAsync) and update the callsite that does if (!isSafeUri(dbUri)) to
await the check before calling mongoose.createConnection(dbUri, ...), ensuring
you strip brackets for IPv6, handle DNS CNAMEs that resolve to multiple
addresses, and treat any restricted resolved address as unsafe.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ecb91d4e-c147-41ab-8c71-576c19258c7d

📥 Commits

Reviewing files that changed from the base of the PR and between 5d34cf8 and 054c535.

📒 Files selected for processing (1)
  • apps/dashboard-api/src/controllers/project.controller.js

Comment thread apps/dashboard-api/src/controllers/project.controller.js Outdated
Copy link
Copy Markdown
Collaborator

@yash-pouranik yash-pouranik left a comment

Choose a reason for hiding this comment

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

please resolve coderabbits comments @anshul23102

@yash-pouranik
Copy link
Copy Markdown
Collaborator

@anshul23102

Add async DNS lookups to detect when hostnames resolve to restricted IP ranges. Implement isRestrictedIPv6 to block link-local (fe80::/10) and ULA (fc00::/7) ranges. Update callsite to await the now-async isSafeUri check.

Signed-off-by: Anshul Jain <anshul23102@iiitd.ac.in>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/dashboard-api/src/controllers/project.controller.js (2)

575-605: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Stop returning raw driver errors from the BYOD validation path.

This branch now sits directly behind the new SSRF check, but it still returns raw { error: ... } payloads and appends connErr.message to the client response. That leaks connection internals from attacker-controlled URIs and breaks the controller contract for dashboard APIs.

As per coding guidelines, "All API endpoints must return response format: { success: bool, data: {}, message: "" }. Use AppError class for errors. Never use raw throw statements or expose MongoDB errors to clients."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/dashboard-api/src/controllers/project.controller.js` around lines 575 -
605, The BYOD validation currently returns raw driver messages from the
mongoose.createConnection error path; change the catch block in the project DB
validation (around isSafeUri, encrypt, mongoose.createConnection, getPublicIp)
to NOT include connErr.message in client responses: log connErr (e.g.,
console.error or logger.error) for diagnostics, and return errors using the
AppError pattern or the controller's standard response shape — e.g., call
next(new AppError("Could not verify MongoDB connection. Please ensure the host
is accessible and whitelisted", 400)) or send res.status(400).json({ success:
false, data: {}, message: "Could not verify MongoDB connection. Please ensure
the host is accessible and whitelisted" }) and only include the whitelisting
hint (with getPublicIp()) as a generic instruction, never raw driver text.

526-559: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

SSRF gate misses mongodb+srv:// SRV-discovered targets.

In apps/dashboard-api/src/controllers/project.controller.js (lines ~526-559), isSafeUri only parses the URI’s provided hostname and blocks it based on direct hostname A/AAAA resolution (plus a small blocked-hostname list). For mongodb+srv://, the MongoDB driver performs SRV seedlist discovery (_mongodb._tcp.<seed>) and connects to the hostnames returned by the SRV records; those SRV-returned targets are not resolved/checked by isSafeUri, so a public “seed” hostname can still yield private/internal SRV targets and bypass the SSRF gate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/dashboard-api/src/controllers/project.controller.js` around lines 526 -
559, isSafeUri currently only checks the parsed hostname and its A/AAAA records
but misses SRV-discovered targets for mongodb+srv:// URIs; update isSafeUri to
detect URL.protocol === "mongodb+srv:" (or scheme startsWith "mongodb+srv") and
perform a dns.resolveSrv("_mongodb._tcp." + host), then for each
SRV.record.target resolve their A/AAAA addresses (using dns.resolve4/resolve6)
and run them through the existing
isRestrictedIPv4/isRestrictedIPv6/isRestrictedIP checks; if the SRV lookup or
any resolution yields a restricted address, return false, and if SRV lookup
fails treat the URI as unsafe (consistent with current A/AAAA failure handling).
Ensure you reference and reuse the existing helper functions isRestrictedIP,
isRestrictedIPv4, isRestrictedIPv6 and DNS functions (dns.resolveSrv,
dns.resolve4, dns.resolve6) and keep the original fallback behavior for
non-mongodb+srv schemes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/dashboard-api/src/controllers/project.controller.js`:
- Around line 509-517: The isRestrictedIPv6 function currently misses the full
fe80::/10 range and treats IPv4-mapped IPv6 (::ffff:...) as public; update
isRestrictedIPv6 to (1) normalize the input as done now, (2) early-handle IPv6
loopback variants (::1, 0:0:0:0:0:0:0:1), (3) detect and treat IPv4-mapped
literals starting with ::ffff: by extracting the embedded IPv4 and calling
whatever IPv4-private check you have (or apply RFC1918/loopback rules), (4)
correctly match the link-local fe80::/10 prefix (not just fe80:) and the ULA
fc00::/7 (checking first 7 bits / prefixes fc00..fdff), and (5) keep existing
bracket stripping and lowercasing; update isRestrictedIPv6 accordingly so
::ffff:127.0.0.1 and any fe9*:.. or febf:* addresses are flagged restricted.
- Around line 540-556: The hostname resolution currently only calls
dns.resolve6(host) if dns.resolve4(host) throws, which allows a dual-stack host
with a safe A record but restricted AAAA to bypass checks; in isSafeUri, call
both dns.resolve4(host) and dns.resolve6(host) (independently, catching errors
for each) and inspect every returned address with isRestrictedIPv4 and
isRestrictedIPv6 respectively, returning false if any restricted address is
found; only allow the hostname when neither resolution produced a restricted
address (and handle the case where both lookups fail per existing policy).

---

Outside diff comments:
In `@apps/dashboard-api/src/controllers/project.controller.js`:
- Around line 575-605: The BYOD validation currently returns raw driver messages
from the mongoose.createConnection error path; change the catch block in the
project DB validation (around isSafeUri, encrypt, mongoose.createConnection,
getPublicIp) to NOT include connErr.message in client responses: log connErr
(e.g., console.error or logger.error) for diagnostics, and return errors using
the AppError pattern or the controller's standard response shape — e.g., call
next(new AppError("Could not verify MongoDB connection. Please ensure the host
is accessible and whitelisted", 400)) or send res.status(400).json({ success:
false, data: {}, message: "Could not verify MongoDB connection. Please ensure
the host is accessible and whitelisted" }) and only include the whitelisting
hint (with getPublicIp()) as a generic instruction, never raw driver text.
- Around line 526-559: isSafeUri currently only checks the parsed hostname and
its A/AAAA records but misses SRV-discovered targets for mongodb+srv:// URIs;
update isSafeUri to detect URL.protocol === "mongodb+srv:" (or scheme startsWith
"mongodb+srv") and perform a dns.resolveSrv("_mongodb._tcp." + host), then for
each SRV.record.target resolve their A/AAAA addresses (using
dns.resolve4/resolve6) and run them through the existing
isRestrictedIPv4/isRestrictedIPv6/isRestrictedIP checks; if the SRV lookup or
any resolution yields a restricted address, return false, and if SRV lookup
fails treat the URI as unsafe (consistent with current A/AAAA failure handling).
Ensure you reference and reuse the existing helper functions isRestrictedIP,
isRestrictedIPv4, isRestrictedIPv6 and DNS functions (dns.resolveSrv,
dns.resolve4, dns.resolve6) and keep the original fallback behavior for
non-mongodb+srv schemes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6f6d4136-90b9-4540-ab91-2fa5a709c66d

📥 Commits

Reviewing files that changed from the base of the PR and between 054c535 and 5e6f492.

📒 Files selected for processing (1)
  • apps/dashboard-api/src/controllers/project.controller.js

Comment thread apps/dashboard-api/src/controllers/project.controller.js
Comment thread apps/dashboard-api/src/controllers/project.controller.js Outdated
…k DNS resolution

Enhance isRestrictedIPv6 to block full fe80::/10 range (fe80: through febf:), detect IPv4-mapped IPv6 addresses (::ffff:x.x.x.x), and block unspecified address (::). Update isSafeUri to resolve both A and AAAA records simultaneously using Promise.allSettled, ensuring dual-stack hostnames with safe A but restricted AAAA records cannot bypass checks.

Signed-off-by: Anshul Jain <anshul23102@iiitd.ac.in>
Add comprehensive JSDoc documentation to all SSRF prevention helper functions:
- ipv4ToInt(): converts dotted-decimal IPv4 to 32-bit integer
- isRestrictedIPv4(): validates against IPv4 restricted ranges (loopback, RFC-1918, cloud metadata, unspecified)
- isRestrictedIPv6(): validates against IPv6 restricted ranges (loopback, link-local, ULA, IPv4-mapped)
- isRestrictedIP(): dispatches validation based on IP version
- isSafeUri(): async DNS-aware URI validation for SSRF prevention

Satisfies CodeRabbit docstring coverage requirement (80% threshold).
@anshul23102
Copy link
Copy Markdown
Contributor Author

Resolved CodeRabbit docstring comments:

Added comprehensive JSDoc documentation to all SSRF validation helper functions:

  • ipv4ToInt(): Converts dotted-decimal IPv4 to 32-bit unsigned integer for range comparisons
  • isRestrictedIPv4(): Validates against IPv4 restricted ranges (loopback, RFC-1918, cloud metadata, unspecified)
  • isRestrictedIPv6(): Validates against IPv6 restricted ranges (loopback, link-local, ULA, IPv4-mapped)
  • isRestrictedIP(): Dispatches IP validation based on version (IPv4/IPv6)
  • isSafeUri(): Async DNS-aware URI validation with dual-stack support for SSRF prevention

Each function now includes:

  • Clear description of what it validates
  • Parameter documentation
  • Return type specification
  • Security context explaining the SSRF threat model

Docstring coverage requirement (80% threshold) is now satisfied. The push updated the PR branch with the new documentation commit.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/dashboard-api/src/controllers/project.controller.js (2)

611-641: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep updateExternalConfig on the controller error contract.

This path still returns raw { error: ... } payloads, and the fallback appends connErr.message directly into the client response. That both violates the shared API contract and exposes MongoDB driver/server details from a user-supplied URI. Please route these failures through AppError, or at minimum return { success: false, data: {}, message } without the raw Mongo error text. As per coding guidelines, "All API endpoints must return response format: { success: bool, data: {}, message: "" }. Use AppError class for errors. Never use raw throw statements or expose MongoDB errors to clients."

Also applies to: 671-677

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/dashboard-api/src/controllers/project.controller.js` around lines 611 -
641, The controller currently returns raw { error: ... } and appends
connErr.message to the client, violating the API contract; update the error path
in the updateExternalConfig flow (the block using isSafeUri, encrypt,
mongoose.createConnection, getPublicIp) to NOT expose raw Mongo errors and to
follow the shared contract: on failure create/throw an AppError or send
res.status(400).json({ success: false, data: {}, message: "<sanitized message>"
}) instead of { error: ... }; remove any direct inclusion of connErr.message in
responses, only include a generic, user-friendly message (e.g. whitelist hint
using getPublicIp when applicable) and use AppError where other controller
errors are raised to keep behavior consistent across the controller.

566-576: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize bracketed IPv6 hostname before net.isIPv6()

new URL(uri).hostname returns IPv6 literals with square brackets (e.g. "[2001:db8::1]"), but net.isIPv6() only returns true for the bracketless form ("2001:db8::1"). Strip [] before the IP checks so IPv6 literals follow the intended restriction logic.

Suggested fix
-    const host = parsed.hostname.toLowerCase();
+    const host = parsed.hostname.replace(/^\[|\]$/g, "").toLowerCase();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/dashboard-api/src/controllers/project.controller.js` around lines 566 -
576, The hostname from new URL(uri) can be a bracketed IPv6 literal (e.g.
"[2001:db8::1]") so update the logic that checks net.isIPv6/net.isIPv4 and calls
isRestrictedIP to first normalize the hostname by stripping surrounding "[" and
"]" if present (use the existing parsed and host variables), then use that
unbracketedHost for net.isIPv6, net.isIPv4 and isRestrictedIP calls; ensure
blockedHostnames check still uses the original lowercased host as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/dashboard-api/src/controllers/project.controller.js`:
- Around line 578-593: isSafeUri currently only resolves parsed.hostname via
dns.resolve4/resolve6 but misses SRV/TXT seedlist discovery used by
mongoose.createConnection for mongodb+srv URIs; update isSafeUri (or caller
updateExternalConfig) to detect mongodb+srv:// and perform dns SRV (and TXT if
needed) resolution for the SRV records, then resolve A/AAAA for each SRV-target
hostname and run isRestrictedIP against each resulting IP, rejecting if any are
restricted — alternatively explicitly reject mongodb+srv URIs in
updateExternalConfig before calling mongoose.createConnection; reference
isSafeUri, updateExternalConfig, and mongoose.createConnection when making the
change.

---

Outside diff comments:
In `@apps/dashboard-api/src/controllers/project.controller.js`:
- Around line 611-641: The controller currently returns raw { error: ... } and
appends connErr.message to the client, violating the API contract; update the
error path in the updateExternalConfig flow (the block using isSafeUri, encrypt,
mongoose.createConnection, getPublicIp) to NOT expose raw Mongo errors and to
follow the shared contract: on failure create/throw an AppError or send
res.status(400).json({ success: false, data: {}, message: "<sanitized message>"
}) instead of { error: ... }; remove any direct inclusion of connErr.message in
responses, only include a generic, user-friendly message (e.g. whitelist hint
using getPublicIp when applicable) and use AppError where other controller
errors are raised to keep behavior consistent across the controller.
- Around line 566-576: The hostname from new URL(uri) can be a bracketed IPv6
literal (e.g. "[2001:db8::1]") so update the logic that checks
net.isIPv6/net.isIPv4 and calls isRestrictedIP to first normalize the hostname
by stripping surrounding "[" and "]" if present (use the existing parsed and
host variables), then use that unbracketedHost for net.isIPv6, net.isIPv4 and
isRestrictedIP calls; ensure blockedHostnames check still uses the original
lowercased host as before.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9ec89a2b-1fb4-4b35-a27b-1d54eb83781c

📥 Commits

Reviewing files that changed from the base of the PR and between 5e6f492 and b3a792e.

📒 Files selected for processing (1)
  • apps/dashboard-api/src/controllers/project.controller.js

Comment thread apps/dashboard-api/src/controllers/project.controller.js
…config

- Strip brackets from IPv6 literals before net.isIPv6() checks to properly validate restricted ranges
- Reject mongodb+srv:// URIs to prevent hidden SRV/TXT resolution bypass of SSRF checks
- Sanitize all error responses to follow API contract and prevent MongoDB error exposure
- Replace raw error payloads with consistent { success, data, message } format

Fixes CodeRabbit security findings on DNS resolution and error handling.

Signed-off-by: Anshul Jain <anshul23102@iiitd.ac.in>
@anshul23102
Copy link
Copy Markdown
Contributor Author

CodeRabbit Security Fixes Applied

✅ SRV Resolution Bypass (Line 593)

Added explicit rejection of mongodb+srv:// URIs in isSafeUri() to prevent hidden SRV/TXT discovery that bypasses SSRF checks.

✅ IPv6 Bracket Normalization (Line 566)

Strip square brackets from parsed IPv6 literals before net.isIPv6() checks: parsed.hostname.replace(/^\\[|\\]$/g, "")

✅ Error Contract Violation (Lines 611-641)

Sanitize all error responses to follow standard API contract { success, data, message } and remove raw MongoDB error exposure. Replaced 5 error returns with safe, generic messages.

All fixes pushed to branch in commit 48e5ab1.

@yash-pouranik
Copy link
Copy Markdown
Collaborator

yash-pouranik commented Jun 5, 2026

Thank you for pointing out this issue @anshul23102
Hope to see more contributions
Are u also in gssoc 26??
Also please leave a star
all the best. ❤️

@yash-pouranik
Copy link
Copy Markdown
Collaborator

yash-pouranik commented Jun 5, 2026

bro is ur github acc made by this mail??
@anshul23102
anshul23102@iiitd.ac.in

If this is the only, then please add ur personal account as well.

@yash-pouranik yash-pouranik merged commit 6109945 into geturbackend:main Jun 5, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] dashboard-api: isSafeUri in project.controller.js does not block RFC-1918 or cloud metadata IP ranges, enabling SSRF via external MongoDB URI

2 participants