Skip to content

test(security): add SSRF validation edge-case coverage#898

Open
areporeporepo wants to merge 2 commits intoNVIDIA:mainfrom
areporeporepo:test/ssrf-validation-edge-cases
Open

test(security): add SSRF validation edge-case coverage#898
areporeporepo wants to merge 2 commits intoNVIDIA:mainfrom
areporeporepo:test/ssrf-validation-edge-cases

Conversation

@areporeporepo
Copy link
Contributor

@areporeporepo areporeporepo commented Mar 25, 2026

Adds 31 new test cases to ssrf.test.ts covering:

  • CIDR boundary precision for all private ranges
  • IPv6 edge cases: link-local, multicast, fd00::/8
  • IPv4-mapped IPv6 boundary tests
  • DNS rebinding: mixed public+private A records
  • URL parsing: data: URI, query params, userinfo

Summary by CodeRabbit

  • Tests
    • Expanded test coverage for request validation to strengthen security: improved IP/address boundary checks (IPv4/IPv6 and mapped addresses), simulated DNS rebinding scenarios to ensure mixed-resolution safety, stricter URL parsing to reject disallowed schemes and accept valid query/fragment/userinfo cases while preserving original URLs.

- CIDR boundary precision for all private ranges
- IPv6 edge cases: link-local, multicast, fd00::/8
- IPv4-mapped IPv6 boundary tests
- DNS rebinding: mixed public+private A records
- URL parsing: data: URI, query params, userinfo

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 25, 2026 18:17
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 25, 2026

📝 Walkthrough

Walkthrough

Added test cases expanding SSRF protections: precise IPv4 CIDR boundary checks, IPv6 edge cases including IPv4-mapped addresses, DNS rebinding simulations with mixed resolved records, and URL parsing edge-case tests (reject data: URIs; accept query, fragment, and userinfo while preserving original URL).

Changes

Cohort / File(s) Summary
SSRF Test Coverage
nemoclaw/src/blueprint/ssrf.test.ts
Added ~115 lines of tests: detailed isPrivateIp assertions for IPv4 CIDR boundaries (10/8, 172.16/12, 192.168/16, 127/8, 169.254/16), IPv6 edge cases (link-local, multicast, ::0, fc00::/7 vs fd00::/8), IPv4-mapped IPv6 handling, validateEndpointUrl DNS rebinding scenarios (mixed public/private resolutions) and URL parsing edge-case checks (reject data: URIs; allow query params, fragments, and basic auth; ensure original URL preserved).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐇 I hopped through tests both sharp and spry,
Checking ranges low and ranges high,
Mapped IPv6 and DNS that swings—
I nibble bugs and chase bad things,
Hooray! Safe endpoints now pass by.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding SSRF validation edge-case test coverage, which aligns perfectly with the changeset of 115 new test assertions in ssrf.test.ts.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

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

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

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds expanded SSRF-related unit test coverage for IP classification and endpoint URL validation in the NemoClaw blueprint SSRF guardrails.

Changes:

  • Add CIDR boundary tests for IPv4 private ranges (10/8, 172.16/12, 169.254/16, 192.168/16 boundaries).
  • Add IPv6 edge-case tests (ULA boundaries and IPv4-mapped IPv6 cases).
  • Add validateEndpointUrl tests for DNS rebinding (mixed A/AAAA results) and URL parsing cases (data: URI, query/fragment, userinfo).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +204 to +208
// link-local and multicast are NOT in PRIVATE_NETWORKS — verify they're treated as public
["fe80::1", false],
["ff02::1", false],
// zero address
["::0", false],
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

The IPv6 expectations here treat link-local (fe80::/10), multicast (ff00::/8), and the unspecified address (::/128) as “public”. For SSRF protection these ranges are non-globally-routable and are commonly treated as internal/reserved, so allowing them can enable access to local interfaces/services. Consider updating isPrivateIp/PRIVATE_NETWORKS to block these reserved ranges and adjust these tests to expect rejection (private=true) accordingly.

Suggested change
// link-local and multicast are NOT in PRIVATE_NETWORKS — verify they're treated as public
["fe80::1", false],
["ff02::1", false],
// zero address
["::0", false],
// link-local, multicast, and unspecified are treated as private/internal for SSRF protection
["fe80::1", true],
["ff02::1", true],
// zero address
["::0", true],

Copilot uses AI. Check for mistakes.
Comment on lines +209 to +213
// fd00::/8 boundaries
["fcff::1", false], // fc00::/8 is NOT protected, only fd00::/8
["fd00::0", true], // first address in fd00::/8
["fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", true], // last address in fd00::/8
["fe00::1", false], // just above fd00::/8
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

This test asserts fc00::/8 (via fcff::1) is allowed, but RFC 4193 Unique Local Addresses are fc00::/7 (covering both fc00::/8 and fd00::/8) and are typically considered private/internal for SSRF defenses. Consider expanding the IPv6 private range to fc00::/7 and updating this expectation to true (and adding a boundary test for fbff:: vs fc00:: if you want precision similar to the IPv4 cases).

Suggested change
// fd00::/8 boundaries
["fcff::1", false], // fc00::/8 is NOT protected, only fd00::/8
["fd00::0", true], // first address in fd00::/8
["fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", true], // last address in fd00::/8
["fe00::1", false], // just above fd00::/8
// fc00::/7 (RFC 4193 Unique Local Addresses) boundaries
["fbff::1", false], // just below fc00::/7
["fcff::1", true], // within fc00::/7 ULA range
["fd00::0", true], // first address in fd00::/8 (within fc00::/7)
["fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", true], // last address in fc00::/7 ULA range
["fe00::1", false], // just above fc00::/7

Copilot uses AI. Check for mistakes.
await expect(validateEndpointUrl(url)).resolves.toBe(url);
});

it("allows URL with basic auth in hostname", async () => {
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

Test name is misleading: https://user:pass@api.example.com/v1 is userinfo/basic auth in the URL (not “in hostname”). Renaming the test description to reflect userinfo will make it clearer what behavior is being validated.

Suggested change
it("allows URL with basic auth in hostname", async () => {
it("allows URL with userinfo/basic auth", async () => {

Copilot uses AI. Check for mistakes.
Copy link
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 the current code and only fix it if needed.

Inline comments:
In `@nemoclaw/src/blueprint/ssrf.test.ts`:
- Around line 203-213: The test's it.each array in ssrf.test.ts currently marks
link-local and multicast IPv6 (e.g., "fe80::1", "ff02::1") and the fc00::/7
boundary example as allowed (false); update those expectations to true so they
are treated as protected by SSRF checks. Locate the it.each block in
ssrf.test.ts and change the boolean for "fe80::1", "ff02::1" and the fc00::/7
example entry (e.g., "fcff::1" or whichever fc00::/7 test row exists) from false
to true to reflect that these scopes are not public.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 52f24342-45f1-4ceb-9568-81b1029079cf

📥 Commits

Reviewing files that changed from the base of the PR and between b789cc2 and 96e23b6.

📒 Files selected for processing (1)
  • nemoclaw/src/blueprint/ssrf.test.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
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

♻️ Duplicate comments (1)
nemoclaw/src/blueprint/ssrf.test.ts (1)

204-206: ⚠️ Potential issue | 🔴 Critical

Do not codify link-local/multicast IPv6 as public in SSRF tests.

Line 205 and Line 206 currently assert fe80::1 and ff02::1 as non-private. That bakes in a security gap rather than guarding against it.

🔧 Suggested expectation update
-    ["fe80::1", false],
-    ["ff02::1", false],
+    ["fe80::1", true], // link-local should be blocked
+    ["ff02::1", true], // multicast should be blocked
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nemoclaw/src/blueprint/ssrf.test.ts` around lines 204 - 206, The test
currently asserts ["fe80::1", false] and ["ff02::1", false] as non-private
(public); update those expectations to treat link-local and multicast IPv6 as
private/blocked by changing both entries to ["fe80::1", true] and ["ff02::1",
true] in the ssrf test data (the array containing those tuples) and add a brief
inline comment noting they are link-local/multicast addresses so the SSRF check
treats them as non-public.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@nemoclaw/src/blueprint/ssrf.test.ts`:
- Around line 210-213: Tests expect the full ULA range (fc00::/7) but the
implementation's PRIVATE_NETWORKS only contains cidr6("fd00::", 8); open
nemoclaw/src/blueprint/ssrf.ts and add cidr6("fc00::", 7) to the
PRIVATE_NETWORKS array (next to the existing cidr6("fd00::", 8)), ensuring the
same type/format and any import/usage of cidr6 remains correct so the ssrf check
(PRIVATE_NETWORKS) blocks the full ULA range that the tests assert.

---

Duplicate comments:
In `@nemoclaw/src/blueprint/ssrf.test.ts`:
- Around line 204-206: The test currently asserts ["fe80::1", false] and
["ff02::1", false] as non-private (public); update those expectations to treat
link-local and multicast IPv6 as private/blocked by changing both entries to
["fe80::1", true] and ["ff02::1", true] in the ssrf test data (the array
containing those tuples) and add a brief inline comment noting they are
link-local/multicast addresses so the SSRF check treats them as non-public.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f28560f3-ae44-4653-a235-0c891b4eb86c

📥 Commits

Reviewing files that changed from the base of the PR and between 96e23b6 and 3607626.

📒 Files selected for processing (1)
  • nemoclaw/src/blueprint/ssrf.test.ts

Comment on lines +210 to +213
["fc00::1", true], // first address in fc00::/7 (ULA)
["fcff::1", true], // still within fc00::/7
["fd00::0", true], // first address in fd00::/8
["fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", true], // last address in fd00::/8
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

fc00::/7 expectations currently conflict with implementation.

Line 210–Line 213 expect fc00::/7 + fd00::/8 to be private, but nemoclaw/src/blueprint/ssrf.ts currently defines only cidr6("fd00::", 8) in PRIVATE_NETWORKS. This will fail unless implementation is updated in the same PR.

🔧 If intent is to block full ULA, update implementation
-  cidr6("fd00::", 8),
+  cidr6("fc00::", 7),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
["fc00::1", true], // first address in fc00::/7 (ULA)
["fcff::1", true], // still within fc00::/7
["fd00::0", true], // first address in fd00::/8
["fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", true], // last address in fd00::/8
cidr6("fc00::", 7),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nemoclaw/src/blueprint/ssrf.test.ts` around lines 210 - 213, Tests expect the
full ULA range (fc00::/7) but the implementation's PRIVATE_NETWORKS only
contains cidr6("fd00::", 8); open nemoclaw/src/blueprint/ssrf.ts and add
cidr6("fc00::", 7) to the PRIVATE_NETWORKS array (next to the existing
cidr6("fd00::", 8)), ensuring the same type/format and any import/usage of cidr6
remains correct so the ssrf check (PRIVATE_NETWORKS) blocks the full ULA range
that the tests assert.

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.

2 participants