Skip to content

[security] Fix double-slash path blocking to return 400 in both Tornado and Starlette#13822

Merged
sfc-gh-nbellante merged 1 commit intodevelopfrom
fix-double-slash-test-status-code
Feb 4, 2026
Merged

[security] Fix double-slash path blocking to return 400 in both Tornado and Starlette#13822
sfc-gh-nbellante merged 1 commit intodevelopfrom
fix-double-slash-test-status-code

Conversation

@sfc-gh-nbellante
Copy link
Copy Markdown
Contributor

@sfc-gh-nbellante sfc-gh-nbellante commented Feb 4, 2026

Summary

  • Added UnsafePathBlockHandler to the Tornado server that blocks double-slash paths with 400 Bad Request, matching the Starlette PathSecurityMiddleware behavior
  • Updated the E2E test test_double_slash_not_redirected_to_external to expect 400 instead of 403
  • Previously, the Starlette middleware (from [security] Prevent SSRF attacks via path traversal in component file handling #13733) returned 400 while Tornado relied on its built-in handling which returned 403, causing the test to fail depending on which server backend was used

Files changed:

  • lib/streamlit/web/server/routes.py — Added UnsafePathBlockHandler class
  • lib/streamlit/web/server/server.py — Registered the handler as the first route
  • e2e_playwright/web_server_test.py — Updated test to expect 400

Test plan

  • web_server_test.py::test_double_slash_not_redirected_to_external passes for both Tornado and Starlette E2E test suites

Copilot AI review requested due to automatic review settings February 4, 2026 17:00
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 4, 2026

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-13822/streamlit-1.53.1-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-13822.streamlit.app (☁️ Deploy here if not accessible)

@sfc-gh-nbellante sfc-gh-nbellante changed the title Fix E2E test to expect 400 from PathSecurityMiddleware instead of 403 [security] Fix E2E test for double-slash path blocking status code Feb 4, 2026
Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io bot commented Feb 4, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@sfc-gh-nbellante sfc-gh-nbellante changed the title [security] Fix E2E test for double-slash path blocking status code [fix] Fix E2E test for double-slash path blocking status code Feb 4, 2026
@sfc-gh-nbellante sfc-gh-nbellante added the starlette-tests If applied to PR, will run all e2e tests with the Starlette implementation. label Feb 4, 2026
Copy link
Copy Markdown
Contributor

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

This PR fixes an E2E test to expect the correct HTTP status code (400 Bad Request) from the PathSecurityMiddleware instead of the previously expected 403 Forbidden.

Changes:

  • Updated test_double_slash_not_redirected_to_external to expect status code 400 instead of 403
  • Updated test comments to accurately reference PathSecurityMiddleware

@sfc-gh-nbellante sfc-gh-nbellante added security-assessment-completed impact:internal PR changes only affect internal code change:bugfix PR contains bug fix implementation labels Feb 4, 2026
@sfc-gh-nbellante sfc-gh-nbellante force-pushed the fix-double-slash-test-status-code branch from 464a7ac to 3302361 Compare February 4, 2026 17:38
@sfc-gh-nbellante sfc-gh-nbellante requested a review from a team as a code owner February 4, 2026 17:38
@sfc-gh-nbellante sfc-gh-nbellante changed the title [fix] Fix E2E test for double-slash path blocking status code [security] Fix double-slash path blocking to return 400 in both Tornado and Starlette Feb 4, 2026
…lette

The PathSecurityMiddleware in Starlette returns 400 Bad Request for
double-slash paths. The Tornado server previously relied on Tornado's
built-in handling which returned 403. Added UnsafePathBlockHandler to
the Tornado server to explicitly block double-slash paths with 400,
matching the Starlette behavior. Updated the E2E test accordingly.
@sfc-gh-nbellante sfc-gh-nbellante force-pushed the fix-double-slash-test-status-code branch from 3302361 to 4173795 Compare February 4, 2026 18:41
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 4, 2026

## Summary

This PR aligns Tornado's handling of double-slash paths with the Starlette middleware by returning 400 for //-prefixed requests and updates the related e2e test expectation.

Code Quality

The new UnsafePathBlockHandler is clear, but its docstring states it blocks UNC and path traversal patterns while the route only matches leading //. Consider tightening the description or expanding the matching logic to avoid misleading documentation (lib/streamlit/web/server/routes.py lines 124-129).

Test Coverage

The e2e test now asserts the 400 response for double-slash paths (e2e_playwright/web_server_test.py lines 578-591). No unit tests were added; for this routing-only change, the e2e coverage is likely sufficient.

Backwards Compatibility

Behavior changes from 403 to 400 for //-prefixed requests. This affects only an error path and should have minimal impact, but any clients asserting 403 would need adjustment.

Security & Risk

This is a security hardening change that blocks protocol-relative path patterns earlier in the request flow. No new security risks or regression hotspots identified.

Accessibility

No frontend/UI changes.

Recommendations

  1. Adjust the UnsafePathBlockHandler docstring to reflect the actual pattern being blocked, or expand matching to cover UNC/path traversal as described (lib/streamlit/web/server/routes.py lines 124-129).

Verdict

APPROVED: Low-risk security hardening with updated test coverage; only a minor docstring consistency note.


This is an automated AI review using gpt-5.2-codex-high. Please verify the feedback and use your judgment.

Copy link
Copy Markdown
Collaborator

@lukasmasuch lukasmasuch left a comment

Choose a reason for hiding this comment

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

LGTM 👍

Comment on lines +373 to +375
# SECURITY: Block unsafe paths (double-slash, UNC paths, etc.)
# before any other handler. Matches PathSecurityMiddleware in Starlette.
(r"^//.*$", UnsafePathBlockHandler),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can you double check with Claude / Cursor that there aren't unintended side-effects with blocking all //?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No unintended side-effects. The // blocking is safe because:

  1. All route construction goes through make_url_path() which strips leading/trailing slashes from both
    the base URL and path segments before joining. It's mathematically impossible for this function to produce a
    // prefix — e.g. make_url_path("", "health")"/health", make_url_path("/app/", "/health")
    "/app/health".

  2. server.baseUrlPath is normalized — even if a user sets it to "/" or "//", slashes are stripped
    before route construction.

  3. The RemoveSlashHandler already has a negative lookahead (?!/) in its regex, explicitly preventing
    matching paths starting with //. Before this PR those paths would 404; now they 400 with a clear message.

  4. WebSocket, static files, media, components, OAuth, upload endpoints — all constructed via
    make_url_path_regex(), all safe.

  5. Starlette already has identical protection in PathSecurityMiddleware. The Tornado
    UnsafePathBlockHandler just brings parity.

  6. The codebase itself acknowledges this — there's a comment in server.py: "if the path starts with a
    double slash //, the redirect will point the browser to the wrong host"
    — confirming // paths were never
    intended to be valid.

The only paths that start with // are malicious inputs (protocol-relative URL attacks like //evil.com).
No legitimate Streamlit functionality produces or requires them.

@sfc-gh-nbellante sfc-gh-nbellante merged commit a3c4ce6 into develop Feb 4, 2026
43 checks passed
@sfc-gh-nbellante sfc-gh-nbellante deleted the fix-double-slash-test-status-code branch February 4, 2026 20:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:bugfix PR contains bug fix implementation impact:internal PR changes only affect internal code starlette-tests If applied to PR, will run all e2e tests with the Starlette implementation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants