Skip to content

Conversation

@richo-anyscale
Copy link
Collaborator

@richo-anyscale richo-anyscale commented Nov 11, 2025

This causes the dashboard to be more thorough in it's attempts to deny browsers access to the job creation APIs

@richo-anyscale richo-anyscale requested a review from a team as a code owner November 11, 2025 22:05
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request enhances security by adding a check for Sec-Fetch-* headers to better identify and block API requests originating from browsers. This is a solid improvement. My review includes suggestions to make the implementation more concise and robust, such as using more idiomatic Python, correcting the HTTP status code for denied requests, and parameterizing the new test to reduce code duplication and improve maintainability.

Comment on lines 140 to 147
def has_sec_fetch_headers(req: Request) -> bool:
"""Checks for the existance of any of the sec-fetch-* headers.
"""
for header in ("Sec-Fetch-Mode", "Sec-Fetch-Dest", "Sec-Fetch-Site", "Sec-Fetch-User"):
if header in req.headers:
return True
return False
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The implementation of has_sec_fetch_headers can be made more concise and Pythonic by using the any() built-in function. This improves readability. Also, there's a typo ('existance') in the docstring, which should be 'existence', and the empty line can be removed for better formatting.

def has_sec_fetch_headers(req: Request) -> bool:
    """Checks for the existence of any of the sec-fetch-* headers."""
    return any(h in req.headers for h in ("Sec-Fetch-Mode", "Sec-Fetch-Dest", "Sec-Fetch-Site", "Sec-Fetch-User"))

Copy link
Collaborator

Choose a reason for hiding this comment

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

I was going to suggest the same thing -- well done, AI overlord!

Comment on lines 460 to 511
def test_deny_fetch_requests(enable_test_module, ray_start_with_dashboard):
assert wait_until_server_available(ray_start_with_dashboard["webui_url"]) is True
webui_url = ray_start_with_dashboard["webui_url"]
webui_url = format_web_url(webui_url)

timeout_seconds = 30
start_time = time.time()
while True:
time.sleep(3)
try:
# Starting and getting jobs should be fine from API clients
response = requests.post(
webui_url + "/api/jobs/", json={"entrypoint": "ls"}
)
response.raise_for_status()
response = requests.get(webui_url + "/api/jobs/")
response.raise_for_status()

# Starting job should be blocked for browsers
response = requests.post(
webui_url + "/api/jobs/",
json={"entrypoint": "ls"},
headers={
"User-Agent": (
"Spurious User Agent"
),
"Sec-Fetch-Site": (
"cross-site"
)
},
)
with pytest.raises(HTTPError):
response.raise_for_status()

# Getting jobs should be fine for browsers
response = requests.get(webui_url + "/api/jobs/")
response.raise_for_status()
break
except (AssertionError, requests.exceptions.ConnectionError) as e:
logger.info("Retry because of %s", e)
finally:
if time.time() > start_time + timeout_seconds:
raise Exception("Timed out while testing.")
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The new test test_deny_fetch_requests is very similar in structure to the existing test_browser_no_post_no_put test. To improve maintainability and avoid code duplication, you could parameterize this test to cover both scenarios (blocking by User-Agent and by Sec-Fetch-* headers). This would allow you to consolidate the logic and eventually remove the test_browser_no_post_no_put test.

@pytest.mark.parametrize(
    "headers_to_block",
    [
        {
            "User-Agent": (
                "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
                "AppleWebKit/537.36 (KHTML, like Gecko) "
                "Chrome/119.0.0.0 Safari/537.36"
            )
        },
        {"User-Agent": "Spurious User Agent", "Sec-Fetch-Site": "cross-site"},
    ],
)
def test_deny_browser_like_requests(
    headers_to_block, enable_test_module, ray_start_with_dashboard
):
    assert wait_until_server_available(ray_start_with_dashboard["webui_url"]) is True
    webui_url = ray_start_with_dashboard["webui_url"]
    webui_url = format_web_url(webui_url)

    timeout_seconds = 30
    start_time = time.time()
    while True:
        time.sleep(3)
        try:
            # Starting and getting jobs should be fine from API clients
            response = requests.post(
                webui_url + "/api/jobs/", json={"entrypoint": "ls"}
            )
            response.raise_for_status()
            response = requests.get(webui_url + "/api/jobs/")
            response.raise_for_status()

            # Starting job should be blocked for browsers
            response = requests.post(
                webui_url + "/api/jobs/",
                json={"entrypoint": "ls"},
                headers=headers_to_block,
            )
            with pytest.raises(HTTPError):
                response.raise_for_status()

            # Getting jobs should be fine for browsers
            response = requests.get(webui_url + "/api/jobs/")
            response.raise_for_status()
            break
        except (AssertionError, requests.exceptions.ConnectionError) as e:
            logger.info("Retry because of %s", e)
        finally:
            if time.time() > start_time + timeout_seconds:
                raise Exception("Timed out while testing.")

Comment on lines 140 to 147
def has_sec_fetch_headers(req: Request) -> bool:
"""Checks for the existance of any of the sec-fetch-* headers.
"""
for header in ("Sec-Fetch-Mode", "Sec-Fetch-Dest", "Sec-Fetch-Site", "Sec-Fetch-User"):
if header in req.headers:
return True
return False
Copy link
Collaborator

Choose a reason for hiding this comment

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

I was going to suggest the same thing -- well done, AI overlord!

Comment on lines 465 to 468
timeout_seconds = 30
start_time = time.time()
while True:
time.sleep(3)
Copy link
Collaborator

Choose a reason for hiding this comment

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

what's the timeout & sleep needed for?

if we need to keep it, can refactor to use the wait_for_condition utility that is used in many other tests

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I just cargo culted the other test, I can try to refactor them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ok, so I did this refactor, but now I'm looking closer at it, I'm betting it's related to whatever we felt the retry logic was needed for (This is before I was around). I moved it out of the loop and we'll see if it becomes flaky.

@ray-gardener ray-gardener bot added the core Issues that should be addressed in Ray Core label Nov 12, 2025
@edoakes edoakes added the go add ONLY when ready to merge, run all tests label Nov 12, 2025
@edoakes
Copy link
Collaborator

edoakes commented Nov 12, 2025

Looks like test is failing in CI: https://buildkite.com/ray-project/microcheck/builds/30837#019a7537-db13-4b75-97c6-0e20cc0c5969/632-2692

Please also sign off the commits to fix DCO build

@richo-anyscale richo-anyscale force-pushed the richo/sec-fetch-headers branch from f69ba0e to 7559f49 Compare November 13, 2025 19:50
@richo-anyscale richo-anyscale force-pushed the richo/sec-fetch-headers branch 2 times, most recently from 1870cfb to 940eb7c Compare November 13, 2025 20:07
@richo-anyscale richo-anyscale force-pushed the richo/sec-fetch-headers branch from 940eb7c to abdd6f1 Compare November 13, 2025 20:11
@richo-anyscale richo-anyscale force-pushed the richo/sec-fetch-headers branch from 2fd984b to 03c1a2f Compare November 13, 2025 20:45
@richo-anyscale richo-anyscale force-pushed the richo/sec-fetch-headers branch from 03c1a2f to 9476253 Compare November 13, 2025 20:48
start_time = time.time()
while True:
time.sleep(3)
wait_for_condition(dashboard_available)
Copy link

Choose a reason for hiding this comment

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

Bug: Uncaught Timeout Error Prevents Proper Retries

The wait_for_condition call inside the retry loop raises RuntimeError on timeout, which isn't caught by the except clause that only handles AssertionError and ConnectionError. This causes the test to fail after 10 seconds even though the outer timeout is 30 seconds, preventing proper retry behavior when the dashboard is slow to start.

Fix in Cursor Fix in Web

@richo-anyscale richo-anyscale force-pushed the richo/sec-fetch-headers branch 3 times, most recently from 2336f38 to 7c31868 Compare November 13, 2025 20:57
@richo-anyscale richo-anyscale force-pushed the richo/sec-fetch-headers branch from 7c31868 to 65e4ce4 Compare November 13, 2025 23:14
Signed-off-by: Richo Healey <[email protected]>
@richo-anyscale richo-anyscale force-pushed the richo/sec-fetch-headers branch from 16e0186 to 28e0a43 Compare November 14, 2025 00:02
@richo-anyscale richo-anyscale force-pushed the richo/sec-fetch-headers branch from fc385ce to f44ce4e Compare November 14, 2025 06:24
@edoakes edoakes merged commit 70e7c72 into master Nov 14, 2025
6 checks passed
@edoakes edoakes deleted the richo/sec-fetch-headers branch November 14, 2025 18:21
@richo-anyscale
Copy link
Collaborator Author

🙌

ArturNiederfahrenhorst pushed a commit to ArturNiederfahrenhorst/ray that referenced this pull request Nov 16, 2025
This causes the dashboard to be more thorough in it's attempts to deny
browsers access to the job creation APIs

---------

Signed-off-by: Richo Healey <[email protected]>
Aydin-ab pushed a commit to Aydin-ab/ray-aydin that referenced this pull request Nov 19, 2025
This causes the dashboard to be more thorough in it's attempts to deny
browsers access to the job creation APIs

---------

Signed-off-by: Richo Healey <[email protected]>
Signed-off-by: Aydin Abiar <[email protected]>
ykdojo pushed a commit to ykdojo/ray that referenced this pull request Nov 27, 2025
This causes the dashboard to be more thorough in it's attempts to deny
browsers access to the job creation APIs

---------

Signed-off-by: Richo Healey <[email protected]>
Signed-off-by: YK <[email protected]>
edoakes pushed a commit to edoakes/ray that referenced this pull request Nov 27, 2025
This causes the dashboard to be more thorough in it's attempts to deny
browsers access to the job creation APIs

---------

Signed-off-by: Richo Healey <[email protected]>
Signed-off-by: Edward Oakes <[email protected]>
aslonnie added a commit that referenced this pull request Nov 28, 2025
Cherry pick #58553 #58648 #59042

---------

Signed-off-by: Richo Healey <[email protected]>
Signed-off-by: Edward Oakes <[email protected]>
Co-authored-by: richo-anyscale <[email protected]>
Co-authored-by: Lonnie Liu <[email protected]>
SheldonTsen pushed a commit to SheldonTsen/ray that referenced this pull request Dec 1, 2025
This causes the dashboard to be more thorough in it's attempts to deny
browsers access to the job creation APIs

---------

Signed-off-by: Richo Healey <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Issues that should be addressed in Ray Core go add ONLY when ready to merge, run all tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants