Skip to content

Add transparent proxy endpoint for RDI native API - #6503

Open
ArtemHoruzhenko wants to merge 1 commit into
mainfrom
feature/rdi-ui/proxy-endpoint-v2
Open

Add transparent proxy endpoint for RDI native API#6503
ArtemHoruzhenko wants to merge 1 commit into
mainfrom
feature/rdi-ui/proxy-endpoint-v2

Conversation

@ArtemHoruzhenko

@ArtemHoruzhenko ArtemHoruzhenko commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • @rdi-ui/pipeline ships its own RDI SDK and calls the native RDI API directly from the browser. This routes it through RedisInsight's own API instead, so instance credentials and self-signed-certificate handling stay server-side and CORS is avoided entirely (the browser only ever talks to RedisInsight).
  • The proxy forwards method/path/query/body/headers to the RDI instance via the existing RdiClient, with hardening applied:
    • Strips hop-by-hop and auth-carrying headers (Authorization, cookies, CSRF token, window-id) in both directions.
    • Rejects non-CRUD HTTP methods and any request path resolving outside the configured RDI instance URL, including attempts to escape it via encoded dot-segments or smuggled query/fragment delimiters.
    • Does not follow upstream redirects server-side; instead rewrites in-scope 3xx Location headers to the externally reachable proxy URL so the browser's follow-up request stays within the proxy, and strips out-of-scope ones.
    • Forwards/returns bodies as raw bytes (arraybuffer) to avoid axios parsing/re-serializing non-JSON, binary, or already-encoded payloads.
    • Forces a sandboxed CSP and nosniff on proxied responses so a compromised RDI instance can't get arbitrary HTML/JS executed under RedisInsight's own origin.

This replaces #6468, which is closed - this branch was recreated with squashed history off latest main (which already includes the merged pipeline-management-v2 standalone-page work) for a clean review.

Test plan

  • npx jest rdi-proxy api.rdi.client - 64/64 tests passing
  • eslint --fix on all touched files - 0 errors
  • tsc --noEmit - no new errors introduced (only pre-existing, unrelated integration-test typing issues)

🤖 Generated with Claude Code


Note

High Risk
Introduces a caller-influenced HTTP proxy to configured RDI instances; overall safety depends on path validation, redirect rewriting, and header filtering holding under edge cases.

Overview
Adds /rdi/:id/proxy/* so the @rdi-ui/pipeline SDK can call the native RDI API through RedisInsight instead of the browser hitting RDI directly—keeping credentials, TLS, and auth on the server and avoiding CORS.

RdiProxyController and RdiProxyService forward method, path (from the raw URL to preserve encoding), query, body, and filtered headers via the existing RdiClient, which gains proxyRequest on ApiRdiClient. Upstream calls use arraybuffer, validateStatus: null, allowAbsoluteUrls: false, and maxRedirects: 0; paths are constrained to the configured RDI base (including subpath escapes via .. / encoded segments), and in-scope 3xx Location values are rewritten to the proxy URL (out-of-scope locations are stripped). The service strips hop-by-hop, auth, cookie, and CORS-related headers and forces Content-Security-Policy: sandbox and X-Content-Type-Options: nosniff on responses. TRACE and CONNECT are rejected at the controller.

New RdiProxyRequest / RdiProxyResponse types and broad unit tests cover the client, controller, and service; RdiModule registers the new controller and provider.

Reviewed by Cursor Bugbot for commit 5c2a7e8. Bugbot is set up for automated code reviews on this repo. Configure here.

@rdi-ui/pipeline ships its own RDI SDK and calls the native RDI API
directly from the browser. Route it through RedisInsight's own API
instead of exposing RDI to the browser directly: this keeps instance
credentials and self-signed-certificate handling server-side, and
avoids CORS entirely since the browser only ever talks to
RedisInsight.

The proxy forwards method/path/query/body/headers to the RDI instance
via the existing RdiClient, with the following hardening applied:

- Strips hop-by-hop and auth-carrying headers (Authorization, cookies,
  CSRF token, window-id) in both directions so the proxy's own
  credentials and RedisInsight's session state never leak to or from
  RDI.
- Rejects non-CRUD HTTP methods and any request path that resolves
  outside the configured RDI instance URL, including attempts to
  escape it via encoded dot-segments or smuggled query/fragment
  delimiters.
- Does not follow upstream redirects server-side (which could let an
  attacker point our backend at an arbitrary host); instead rewrites
  in-scope 3xx Location headers to the externally reachable proxy URL
  so the browser's follow-up request stays within the proxy, and
  strips out-of-scope ones.
- Forwards/returns bodies as raw bytes (arraybuffer) to avoid axios
  parsing and re-serializing non-JSON, binary, or already-encoded
  payloads.
- Forces a sandboxed Content-Security-Policy and nosniff on proxied
  responses so a compromised RDI instance can't get arbitrary HTML/JS
  executed under RedisInsight's own origin.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ArtemHoruzhenko
ArtemHoruzhenko requested a review from a team as a code owner September 14, 2026 09:01

@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: 5c2a7e885f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

method: req.method,
path: RdiProxyController.getUpstreamPath(req, rdiClientMetadata.id),
query: RdiProxyController.getQueryString(req),
body: req.body,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve non-JSON request bodies

For requests such as text/plain, application/octet-stream, or multipart/form-data, req.body is undefined because the application only installs JSON and URL-encoded parsers in main.ts:65-71. The proxy consequently forwards an empty body while retaining the caller's content type, so native RDI endpoints accepting YAML, binary, or multipart payloads cannot work through this otherwise transparent proxy; capture raw bytes or stream the request for content types the existing parsers do not handle.

Useful? React with 👍 / 👎.


let resolved: URL;
try {
resolved = new URL(location, baseUrl);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve relative redirects against the requested upstream URL

When RDI returns a path-relative or query-relative Location, resolving it against the configured base URL changes its meaning. For example, while proxying http://host/rdi/api/v1/pipelines/old, Location: new should resolve to /rdi/api/v1/pipelines/new, but this code resolves it against http://host/rdi and produces /new, which is then stripped as out of scope (or rewritten to the wrong endpoint when there is no subpath). Pass the actual upstream request URL into this method and use it as the resolution base.

Useful? React with 👍 / 👎.

@github-actions

Copy link
Copy Markdown
Contributor

Code Coverage - Backend unit tests

St.
Category Percentage Covered / Total
🟢 Statements 93.28% 16892/18109
🟡 Branches 75.69% 5435/7181
🟢 Functions 87.76% 2595/2957
🟢 Lines 93.14% 16157/17347

Test suite run success

3851 tests passing in 330 suites.

Report generated by 🧪jest coverage report action from 5c2a7e8

@github-actions

Copy link
Copy Markdown
Contributor

Code Coverage - Integration Tests

Status Category Percentage Covered / Total
🟡 Statements 79.51% 18570/23354
🟡 Branches 62.01% 8693/14018
🟡 Functions 67.29% 2516/3739
🟡 Lines 79.1% 17487/22105

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5c2a7e8. Configure here.

'proxy',
);

return `${posix.join(proxyPrefix, relativePath)}${resolved.search}${resolved.hash}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Redirect Location uses the wrong base

Medium Severity

rewriteLocationThroughProxy resolves a redirect Location against rdi.url rather than the request URL that returned the 3xx. Path-relative and query-only values therefore lose the original path, get rewritten to the wrong proxy URL, or are stripped, so the browser cannot follow in-scope redirects through the proxy.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5c2a7e8. Configure here.

// overridden below with FORCED_RESPONSE_HEADERS, not just stripped
'content-security-policy',
'x-content-type-options',
]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Origin-wide response headers still forwarded

Medium Severity

STRIPPED_RESPONSE_HEADERS drops clear-site-data because browsers honor it on any same-origin response, but it still forwards other origin-scoped headers such as strict-transport-security, content-security-policy-report-only, report-to, and nel. A compromised RDI instance can therefore set HSTS or leak request URLs via reporting on RedisInsight's origin.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5c2a7e8. Configure here.

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