mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-01-17 06:21:22 -05:00
fix: prevent XSS via javascript: URIs in recipe actions (#6885)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
|
||||
from pydantic import UUID4, ConfigDict
|
||||
from pydantic import UUID4, ConfigDict, field_validator
|
||||
|
||||
from mealie.schema._mealie import MealieModel
|
||||
from mealie.schema.response.pagination import PaginationBase
|
||||
@@ -22,6 +22,14 @@ class CreateGroupRecipeAction(MealieModel):
|
||||
|
||||
model_config = ConfigDict(use_enum_values=True)
|
||||
|
||||
@field_validator("url")
|
||||
def validate_url_scheme(url: str) -> str:
|
||||
"""Validate that the URL uses a safe scheme to prevent XSS via javascript: URIs."""
|
||||
url_lower = url.lower().strip()
|
||||
if not (url_lower.startswith("http://") or url_lower.startswith("https://")):
|
||||
raise ValueError("URL must use http or https scheme")
|
||||
return url
|
||||
|
||||
|
||||
class SaveGroupRecipeAction(CreateGroupRecipeAction):
|
||||
group_id: UUID4
|
||||
|
||||
@@ -25,7 +25,7 @@ def create_action(action_type: GroupRecipeActionType = GroupRecipeActionType.lin
|
||||
return CreateGroupRecipeAction(
|
||||
action_type=action_type,
|
||||
title=random_string(),
|
||||
url=random_string(),
|
||||
url=f"https://example.com/{random_string()}",
|
||||
)
|
||||
|
||||
|
||||
@@ -194,3 +194,40 @@ def test_group_recipe_actions_trigger_invalid_type(api_client: TestClient, uniqu
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"url,should_pass",
|
||||
[
|
||||
("https://example.com", True),
|
||||
("http://example.com", True),
|
||||
("HTTPS://EXAMPLE.COM", True),
|
||||
("HTTP://EXAMPLE.COM", True),
|
||||
("javascript:alert('xss')", False),
|
||||
("JAVASCRIPT:alert('xss')", False),
|
||||
("data:text/html,<script>alert('xss')</script>", False),
|
||||
("file:///etc/passwd", False),
|
||||
("ftp://example.com", False),
|
||||
("//example.com", False),
|
||||
("example.com", False),
|
||||
],
|
||||
)
|
||||
def test_group_recipe_actions_url_scheme_validation(
|
||||
api_client: TestClient, unique_user: TestUser, url: str, should_pass: bool
|
||||
):
|
||||
"""Test that only http and https URLs are allowed to prevent XSS via javascript: URIs."""
|
||||
action_data = {
|
||||
"action_type": "link",
|
||||
"title": random_string(),
|
||||
"url": url,
|
||||
}
|
||||
response = api_client.post(
|
||||
api_routes.households_recipe_actions,
|
||||
json=action_data,
|
||||
headers=unique_user.token,
|
||||
)
|
||||
|
||||
if should_pass:
|
||||
assert response.status_code == 201
|
||||
else:
|
||||
assert response.status_code == 422
|
||||
|
||||
Reference in New Issue
Block a user