mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-05-26 03:30:26 -04:00
fix: Protect sensitive data in query filter API (GHSA-8m57-7cv5-rjp8) (#7629)
Co-authored-by: Hayden <64056131+hay-kot@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
import pytest
|
||||
import sqlalchemy as sa
|
||||
|
||||
from mealie.db.models.recipe.recipe import RecipeModel
|
||||
from mealie.db.models.users.users import LongLiveToken, User
|
||||
from mealie.services.query_filter.builder import (
|
||||
LogicalOperator,
|
||||
NonFilterableValueError,
|
||||
QueryFilterBuilder,
|
||||
QueryFilterJSON,
|
||||
QueryFilterJSONPart,
|
||||
@@ -74,3 +80,80 @@ def test_query_filter_builder_json_uses_raw_value():
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# FilterableColumn tests
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_non_filterable_field_user_password_raises():
|
||||
"""Filtering on User.password (plain Mapped, not FilterableColumn) should raise ValueError."""
|
||||
with pytest.raises(NonFilterableValueError):
|
||||
QueryFilterBuilder.get_model_and_model_attr_from_attr_string("password", User)
|
||||
|
||||
|
||||
def test_non_filterable_field_user_email_raises():
|
||||
"""Filtering on User.email (plain Mapped, not FilterableColumn) should raise ValueError."""
|
||||
with pytest.raises(NonFilterableValueError):
|
||||
QueryFilterBuilder.get_model_and_model_attr_from_attr_string("email", User)
|
||||
|
||||
|
||||
def test_non_filterable_field_long_live_token_raises():
|
||||
"""Filtering on LongLiveToken.token (plain Mapped, not FilterableColumn) should raise ValueError."""
|
||||
with pytest.raises(NonFilterableValueError):
|
||||
QueryFilterBuilder.get_model_and_model_attr_from_attr_string("token", LongLiveToken)
|
||||
|
||||
|
||||
def test_filterable_field_does_not_raise():
|
||||
"""Filtering on a FilterableColumn field should not raise."""
|
||||
model, attr, _ = QueryFilterBuilder.get_model_and_model_attr_from_attr_string("full_name", User)
|
||||
assert model is User
|
||||
assert attr is User.full_name
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Relationship traversal tests
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_deep_traversal_to_filterable_field_works():
|
||||
"""Traversing a relationship to a FilterableColumn field should succeed."""
|
||||
model, attr, _ = QueryFilterBuilder.get_model_and_model_attr_from_attr_string("user.full_name", RecipeModel)
|
||||
assert model is User
|
||||
assert attr is User.full_name
|
||||
|
||||
|
||||
def test_deep_traversal_to_non_filterable_field_raises():
|
||||
"""Traversing a relationship to a plain Mapped field should raise ValueError."""
|
||||
with pytest.raises(NonFilterableValueError):
|
||||
QueryFilterBuilder.get_model_and_model_attr_from_attr_string("user.email", RecipeModel)
|
||||
|
||||
|
||||
def test_deep_traversal_user_password_raises():
|
||||
"""Traversing RecipeModel.user.password should raise ValueError."""
|
||||
with pytest.raises(NonFilterableValueError):
|
||||
QueryFilterBuilder.get_model_and_model_attr_from_attr_string("user.password", RecipeModel)
|
||||
|
||||
|
||||
def test_filter_query_user_email_raises():
|
||||
"""filter_query on user.email should raise ValueError."""
|
||||
query = sa.select(RecipeModel)
|
||||
builder = QueryFilterBuilder('user.email = "test@example.com"')
|
||||
with pytest.raises(NonFilterableValueError):
|
||||
builder.filter_query(query, RecipeModel)
|
||||
|
||||
|
||||
def test_filter_query_user_password_raises():
|
||||
"""filter_query on user.password should raise ValueError."""
|
||||
query = sa.select(RecipeModel)
|
||||
builder = QueryFilterBuilder('user.password = "secret"')
|
||||
with pytest.raises(NonFilterableValueError):
|
||||
builder.filter_query(query, RecipeModel)
|
||||
|
||||
|
||||
def test_association_proxy_resolving_to_filterable_field_works():
|
||||
"""Single-hop association proxy (e.g. household_id) resolving to a FilterableColumn should succeed."""
|
||||
model, attr, _ = QueryFilterBuilder.get_model_and_model_attr_from_attr_string("household_id", RecipeModel)
|
||||
assert model is User
|
||||
assert attr is User.household_id
|
||||
|
||||
@@ -13,8 +13,8 @@ from mealie.db.models.household.household import Household
|
||||
from mealie.db.models.household.household_to_recipe import HouseholdToRecipe
|
||||
from mealie.db.models.household.mealplan import GroupMealPlanRules
|
||||
from mealie.db.models.household.shopping_list import ShoppingList
|
||||
from mealie.db.models.labels import MultiPurposeLabel
|
||||
from mealie.db.models.recipe.ingredient import IngredientFoodModel, IngredientUnitModel
|
||||
from mealie.db.models.recipe.labels import MultiPurposeLabel
|
||||
from mealie.db.models.recipe.recipe import RecipeModel
|
||||
from mealie.db.models.recipe.tool import Tool
|
||||
from mealie.db.models.users.user_to_recipe import UserToRecipe
|
||||
|
||||
Reference in New Issue
Block a user