Skip to content

Require same-origin stream commands (#1355)#1

Merged
Karen86Tonoyan merged 1 commit into
Karen86Tonoyan:mainfrom
vercel-labs:main
May 19, 2026
Merged

Require same-origin stream commands (#1355)#1
Karen86Tonoyan merged 1 commit into
Karen86Tonoyan:mainfrom
vercel-labs:main

Conversation

@Karen86Tonoyan

@Karen86Tonoyan Karen86Tonoyan commented May 18, 2026

Copy link
Copy Markdown
Owner
  • Require same-origin stream commands

Protect the per-session command relay from browser-originated cross-origin requests while preserving same-origin dashboard access.

  • Harden stream command origin checks

Require command relay requests to come from loopback same-origin metadata and prevent request bodies from spoofing security headers.


Summary by Sourcery

Enforce strict same-origin and loopback-only validation for stream command HTTP requests and adjust CORS responses accordingly to harden the command relay against cross-origin abuse.

Bug Fixes:

  • Reject /api/command POST and OPTIONS requests whose Origin or Referer do not match the loopback Host authority, preventing DNS rebinding and cross-origin command injection.
  • Ignore header-like lines in HTTP bodies so request payloads cannot spoof security-related headers like Origin or Referer.

Enhancements:

  • Introduce normalized origin and host authority parsing helpers, including loopback detection, to support robust same-origin checks for stream commands.
  • Tailor CORS headers for /api/command responses to only reflect allowed same-origin origins instead of using wildcard CORS.

Tests:

  • Add unit tests for HTTP handler origin enforcement logic, covering cross-origin rejection, missing metadata, DNS rebinding, and header spoofing scenarios.
  • Add (ignored) end-to-end test ensuring stream commands require same-origin metadata before being relayed to the daemon and that allowed requests propagate correctly without wildcard CORS.

Summary by cubic

Enforce same-origin, loopback-only checks for stream command requests to block cross-origin abuse while keeping same-origin dashboard access working. CORS for /api/command now reflects only allowed origins (no wildcard).

  • Bug Fixes

    • Reject /api/command POST/OPTIONS when Origin or Referer don’t match the loopback Host (prevents DNS rebinding and cross-origin command injection).
    • Ignore header-like lines in request bodies to stop spoofing of Origin/Referer.
    • Return CORS headers for /api/command only when allowed and same-origin; never *.
  • Refactors

    • Add helpers to parse/normalize origin and host authorities and detect loopback.
    • Update HTTP handler to enforce strict same-origin and return JSON errors without CORS for forbidden requests.
    • Add unit tests and an ignored e2e test covering rejection, rebinding, spoofing, and successful same-origin relay.

Written for commit 55f38f4. Summary will update on new commits. Review in cubic

* Require same-origin stream commands

Protect the per-session command relay from browser-originated cross-origin requests while preserving same-origin dashboard access.

Co-authored-by: Muhtasham <20128202+Muhtasham@users.noreply.github.com>

* Harden stream command origin checks

Require command relay requests to come from loopback same-origin metadata and prevent request bodies from spoofing security headers.

Co-authored-by: Muhtasham <20128202+Muhtasham@users.noreply.github.com>

---------

Co-authored-by: Muhtasham <20128202+Muhtasham@users.noreply.github.com>
@sourcery-ai

sourcery-ai Bot commented May 18, 2026

Copy link
Copy Markdown

Reviewer's Guide

Enforces that /api/command stream control requests are only accepted from same-origin loopback browser contexts, tightens origin/host validation, adjusts CORS behavior specifically for command relay, and adds unit/e2e coverage to ensure cross-origin or spoofed requests are rejected before reaching the daemon.

Flow diagram for /api/command same-origin enforcement

flowchart TD
    A[handle_http_request for /api/command] --> B[is_same_origin_command_request]
    B -->|false| C[write_json_error_response_no_cors<br/>HTTP 403 Forbidden]
    C --> D[Close connection]
    B -->|true| E[Read body and relay to daemon]
    E --> F[command_cors_headers<br/>reflect same-origin Origin]
    F --> G[Send JSON response without wildcard CORS]
Loading

File-Level Changes

Change Details Files
Introduce strict same-origin and loopback validation for /api/command requests and tailor CORS headers accordingly.
  • Refactor HTTP header parsing to safely extract header sections and specific header values from peeked requests, avoiding body spoofing of security headers.
  • Add origin/host normalization helpers (including IPv6 and default-port handling) and loopback detection based on authority hostnames.
  • Implement same-origin checks that compare Origin/Referer authorities against the Host header and require both to resolve to loopback hosts.
  • Gate /api/command POST and OPTIONS handling on same-origin checks, returning 403 JSON errors without CORS headers when validation fails.
  • Switch /api/command responses to use per-request CORS headers that reflect the allowed origin instead of the global wildcard CORS headers.
cli/src/native/stream/http.rs
Add unit tests to verify command relay security behavior and prevent regressions in origin handling.
  • Introduce a test helper to send raw HTTP requests into the stream HTTP handler and capture responses.
  • Add a fake daemon implementation (Unix socket) to confirm when commands are or are not relayed to the underlying daemon.
  • Cover scenarios including cross-origin POSTs and preflights, missing Origin/Referer, DNS-rebinding style hosts, header-like lines in the body, and successful same-origin POSTs with appropriate CORS headers.
cli/src/native/stream/http.rs
Add end-to-end tests around stream command same-origin enforcement prior to daemon relay.
  • Add a raw TCP HTTP client helper to talk directly to the stream server in e2e tests.
  • Introduce a fake daemon socket helper for e2e, mirroring the unit-test daemon relay behavior.
  • Add an ignored e2e test that enables streaming, performs a failing cross-origin /api/command POST, then a successful same-origin POST, asserting CORS headers and that only the latter reaches the daemon, and finally disables streaming.
cli/src/native/e2e_tests.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai 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.

Hey - I've found 1 issue, and left some high level feedback:

  • In authority_host, the slice &authority[..=bracket_end + 1] uses an index relative to the stripped string, so for bracketed IPv6 with a port (e.g. [::1]:7777) it will incorrectly include the colon; you likely want to offset the index by the leading [ (e.g. compute the position of ] in authority or adjust by +1 only once) so that the host portion is just the bracketed address.
  • The fake-daemon helpers (spawn_fake_daemon in http.rs tests and spawn_fake_daemon_socket plus the raw HTTP helpers in e2e_tests.rs) are very similar; consider extracting shared helpers into a common test utility to reduce duplication and keep daemon relay behavior consistent across tests.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `authority_host`, the slice `&authority[..=bracket_end + 1]` uses an index relative to the stripped string, so for bracketed IPv6 with a port (e.g. `[::1]:7777`) it will incorrectly include the colon; you likely want to offset the index by the leading `[` (e.g. compute the position of `]` in `authority` or adjust by `+1` only once) so that the host portion is just the bracketed address.
- The fake-daemon helpers (`spawn_fake_daemon` in `http.rs` tests and `spawn_fake_daemon_socket` plus the raw HTTP helpers in `e2e_tests.rs`) are very similar; consider extracting shared helpers into a common test utility to reduce duplication and keep daemon relay behavior consistent across tests.

## Individual Comments

### Comment 1
<location path="cli/src/native/e2e_tests.rs" line_range="118-127" />
<code_context>
+    String::from_utf8(response).expect("HTTP response should be utf-8")
+}
+
+#[cfg(unix)]
+async fn spawn_fake_daemon_socket(
+    socket_dir: &std::path::Path,
</code_context>
<issue_to_address>
**question (testing):** Clarify or revisit the #[ignore] on the end-to-end same-origin stream command test

Since this test is `#[ignore]`, CI won’t detect regressions in same-origin behavior. If it’s ignored due to flakiness or environment constraints, please either document that reason in a comment or factor out a smaller, reliable e2e check (for example, just the rejection path) that can run in CI while preserving coverage of this behavior.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +118 to +127
#[cfg(unix)]
async fn spawn_fake_daemon_socket(
socket_dir: &std::path::Path,
session_name: &str,
) -> tokio::sync::oneshot::Receiver<String> {
use tokio::io::AsyncBufReadExt;

let socket_path = socket_dir.join(format!("{session_name}.sock"));
let _ = std::fs::remove_file(&socket_path);
let listener =

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

question (testing): Clarify or revisit the #[ignore] on the end-to-end same-origin stream command test

Since this test is #[ignore], CI won’t detect regressions in same-origin behavior. If it’s ignored due to flakiness or environment constraints, please either document that reason in a comment or factor out a smaller, reliable e2e check (for example, just the rejection path) that can run in CI while preserving coverage of this behavior.

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 2 files

Re-trigger cubic

@Karen86Tonoyan Karen86Tonoyan merged commit c5ea6c5 into Karen86Tonoyan:main May 19, 2026
2 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