mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-10-16 02:51:56 +00:00
Potential fix for code scanning alert no. 6: URL redirection from remote source (#1406)
* Potential fix for code scanning alert no. 6: URL redirection from remote source Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * fix: reject scheme-bearing EOSdash redirect paths --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
parent
812aa3dda9
commit
1f2faf3f7e
@@ -10,7 +10,7 @@ import traceback
|
|||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Annotated, Any, AsyncGenerator, Dict, List, Optional, Union
|
from typing import Annotated, Any, AsyncGenerator, Dict, List, Optional, Union
|
||||||
from urllib.parse import urlsplit
|
from urllib.parse import urljoin, urlsplit
|
||||||
|
|
||||||
import psutil
|
import psutil
|
||||||
import uvicorn
|
import uvicorn
|
||||||
@@ -2387,7 +2387,13 @@ def _sanitize_redirect_path(path: str) -> Optional[str]:
|
|||||||
depth += 1
|
depth += 1
|
||||||
if depth < 0:
|
if depth < 0:
|
||||||
return None
|
return None
|
||||||
return "/".join(parts)
|
safe_path = "/".join(parts)
|
||||||
|
# urljoin treats a colon in the first segment as a scheme, even without "://".
|
||||||
|
# Check after normalization, which can expose a scheme hidden by "./".
|
||||||
|
parsed = urlsplit(safe_path)
|
||||||
|
if parsed.scheme or parsed.netloc:
|
||||||
|
return None
|
||||||
|
return safe_path
|
||||||
|
|
||||||
|
|
||||||
def _trusted_request_hosts() -> set[str]:
|
def _trusted_request_hosts() -> set[str]:
|
||||||
@@ -2480,8 +2486,10 @@ def redirect(request: Request, path: str) -> Union[HTMLResponse, RedirectRespons
|
|||||||
if get_config().server.eosdash_port:
|
if get_config().server.eosdash_port:
|
||||||
if base_url is None:
|
if base_url is None:
|
||||||
return _eosdash_unknown_page(request, 404)
|
return _eosdash_unknown_page(request, 404)
|
||||||
safe_path = _sanitize_redirect_path(path) or ""
|
safe_path = _sanitize_redirect_path(path)
|
||||||
url = f"{base_url}/{safe_path}"
|
if safe_path is None:
|
||||||
|
return _eosdash_unknown_page(request, 404)
|
||||||
|
url = urljoin(f"{base_url.rstrip('/')}/", safe_path)
|
||||||
return RedirectResponse(url=url, status_code=303)
|
return RedirectResponse(url=url, status_code=303)
|
||||||
|
|
||||||
# Redirect the root URL to the site map
|
# Redirect the root URL to the site map
|
||||||
|
|||||||
@@ -464,6 +464,31 @@ class TestEosdashRedirect:
|
|||||||
assert response.status_code == HTTPStatus.SEE_OTHER
|
assert response.status_code == HTTPStatus.SEE_OTHER
|
||||||
assert response.headers["location"] == "http://localhost:8504/eosdash/health"
|
assert response.headers["location"] == "http://localhost:8504/eosdash/health"
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"path",
|
||||||
|
[
|
||||||
|
"/eosdash:payload",
|
||||||
|
"/eosdash+custom:payload",
|
||||||
|
"/eosdash:%09payload",
|
||||||
|
"/eosdash:%0Apayload",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_scheme_bearing_path_is_rejected(self, client, path):
|
||||||
|
"""User paths must not replace the configured dashboard URL with a scheme."""
|
||||||
|
response = client.get(path, headers={"Host": "localhost:8503"})
|
||||||
|
assert response.status_code == HTTPStatus.NOT_FOUND
|
||||||
|
assert "location" not in response.headers
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"path",
|
||||||
|
["eosdash:payload", "./eosdash:payload", "eosdash:\tpayload", "eosdash:\npayload"],
|
||||||
|
)
|
||||||
|
def test_sanitizer_rejects_schemes_after_normalization(self, path):
|
||||||
|
"""Normalization must not turn an accepted path into a scheme-bearing URL."""
|
||||||
|
from akkudoktoreos.server.eos import _sanitize_redirect_path
|
||||||
|
|
||||||
|
assert _sanitize_redirect_path(path) is None
|
||||||
|
|
||||||
def test_unknown_path_error_page_links_to_request_host(self, client):
|
def test_unknown_path_error_page_links_to_request_host(self, client):
|
||||||
"""The 404 page links to EOSdash on the host the client used."""
|
"""The 404 page links to EOSdash on the host the client used."""
|
||||||
response = client.get("/no-such-page", headers={"Host": "localhost:8503"})
|
response = client.get("/no-such-page", headers={"Host": "localhost:8503"})
|
||||||
|
|||||||
Reference in New Issue
Block a user