Skip to content

fix(security): rebuild proxy URL from trusted endpoint (py/full-ssrf)#100

Merged
marcusds merged 2 commits into
mainfrom
fix-codeql-issues-batch-3/mschwab
May 28, 2026
Merged

fix(security): rebuild proxy URL from trusted endpoint (py/full-ssrf)#100
marcusds merged 2 commits into
mainfrom
fix-codeql-issues-batch-3/mschwab

Conversation

@marcusds
Copy link
Copy Markdown
Contributor

@marcusds marcusds commented May 28, 2026

Summary

Batch 3 of the CodeQL cleanup. Addresses the 1 critical py/full-ssrf alert that surfaced after #91 cleared the related py/partial-ssrf.

Alert addressed

Severity Rule # Problem How we addressed it
Critical py/full-ssrf 1 plugins/nemo-agents/.../api/v2/gateway.pyclient.stream(url=target_url, ...) where target_url was the result of urljoin(endpoint, user_path). The previous py/partial-ssrf fix validated the joined URL's scheme/netloc against the trusted deployment endpoint, but CodeQL upgraded the finding because the full URL still flowed directly from a user-supplied path component into the request. Rebuild target_url via urlunparse((endpoint.scheme, endpoint.netloc, joined.path, joined.params, joined.query, "")) after the existing scheme/netloc match check. The host portion now definitely comes from the DB-loaded deployment endpoint instead of from the user-supplied path text, breaking the full-URL taint flow. Added an upfront 500 if the deployment's stored endpoint has no scheme/netloc so a malformed record can't sneak past the guard.

Test plan

Stacking

This branch is off main. PRs #91 is merged; #98 is still open. No conflicts expected with either.

Summary by CodeRabbit

  • Bug Fixes
    • Improved gateway URL validation to more robustly handle upstream target URL construction. The proxy now validates endpoint configuration and rejects requests with invalid scheme or host changes, enhancing reliability and security of request routing.

Review Change Stack

marcusds added 2 commits May 28, 2026 15:13
…ll-ssrf

The previous py/partial-ssrf fix validated the joined URL's scheme and
netloc against the trusted deployment endpoint, but CodeQL upgraded the
finding to py/full-ssrf because the final target_url still flowed
directly from urljoin(endpoint, user_path) into client.stream(url=...).

Rebuild target_url from urlunparse() using the trusted endpoint's scheme
and netloc, with only path/params/query carried over from the joined URL.
The validation still raises 400 for mismatches; the rebuild step makes
the host portion definitely come from the DB-loaded endpoint, breaking
the full-URL taint flow.

Also added an upfront sanity check that rejects empty scheme/netloc on
the endpoint so a malformed deployment record can't degrade the guard.

Signed-off-by: mschwab <mschwab@nvidia.com>
Signed-off-by: mschwab <mschwab@nvidia.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 28, 2026

Suite Lines Covered Line Rate Branch Rate
Unit Tests 18295/24245 75.5% 61.9%
Integration Tests 11693/23022 50.8% 26.0%

@marcusds marcusds marked this pull request as ready for review May 28, 2026 22:38
@marcusds marcusds requested review from a team as code owners May 28, 2026 22:38
@marcusds marcusds enabled auto-merge May 28, 2026 22:38
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

📝 Walkthrough

Walkthrough

Gateway proxy target URL validation is refactored to robustly validate the configured endpoint, join request paths, and reject requests that attempt to change the upstream scheme or host. The logic now validates endpoint configuration upfront, rebuilds the final URL via urlunparse, and clears fragments.

Changes

Proxy Target URL Validation

Layer / File(s) Summary
Proxy target URL validation and construction
plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py
urlunparse import added. Target URL logic now validates endpoint has scheme and netloc, joins endpoint base with trailing_uri, rejects targets that change scheme or host, and rebuilds via urlunparse with empty fragment.
🚥 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 Title directly addresses the main change: fixing an SSRF security vulnerability by rebuilding the proxy URL from the trusted endpoint, which matches the core objective.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-codeql-issues-batch-3/mschwab

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

Copy link
Copy Markdown

@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: 0

🧹 Nitpick comments (1)
plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py (1)

169-170: 💤 Low value

Pre-existing edge case: query string collision.

If trailing_uri contains an encoded ? (e.g., path%3Fx=1), joined.query will be non-empty. Appending ?{request.url.query} then produces a malformed URL with two ? characters. Not introduced by this PR, but may warrant a future fix.

Example fix
-    if request.url.query:
-        target_url = f"{target_url}?{request.url.query}"
+    if request.url.query:
+        sep = "&" if joined.query else "?"
+        target_url = f"{target_url}{sep}{request.url.query}"
🤖 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 `@plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py` around lines
169 - 170, The current append logic blindly adds "?{request.url.query}" which
produces malformed URLs when the target already contains a query or an encoded
'?' in trailing_uri; update the conditional to detect existing queries by
checking joined.query or the presence of '?' in target_url or '%3F' in
trailing_uri and, when any are true, append "&{request.url.query}" instead of
"?{request.url.query}"; reference the variables target_url, request.url.query,
trailing_uri and joined.query in the fix.
🤖 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.

Nitpick comments:
In `@plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py`:
- Around line 169-170: The current append logic blindly adds
"?{request.url.query}" which produces malformed URLs when the target already
contains a query or an encoded '?' in trailing_uri; update the conditional to
detect existing queries by checking joined.query or the presence of '?' in
target_url or '%3F' in trailing_uri and, when any are true, append
"&{request.url.query}" instead of "?{request.url.query}"; reference the
variables target_url, request.url.query, trailing_uri and joined.query in the
fix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a63e4b0d-8b8b-4b42-aae5-d0c6d2b064a5

📥 Commits

Reviewing files that changed from the base of the PR and between 397e25e and 3e47c37.

📒 Files selected for processing (1)
  • plugins/nemo-agents/src/nemo_agents_plugin/api/v2/gateway.py

@marcusds marcusds added this pull request to the merge queue May 28, 2026
Merged via the queue into main with commit 1a61900 May 28, 2026
23 checks passed
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