Skip to content

Commit a03bae1

Browse files
authored
fix vulnerability by column filtering (#1557)
* fix vulnerability by column filtering * fix lint * fix release notes * bump to full has breaking change
1 parent ef95d32 commit a03bae1

5 files changed

Lines changed: 98 additions & 4 deletions

File tree

docs/releases.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,28 @@
11
# Release notes
22

3+
## 0.23.0
4+
5+
### ‼️🚨 Critical vulnerability fixed – please upgrade ASAP
6+
7+
* In this version of ormar the critical vulnerability (`CVE-2026-26198`) in aggregate functions was patched - thanks @AAtomical
8+
for reporting. The vulnerability was caused by the way ormar generated SQL queries for aggregate functions, allowing arbitrary SQL execution through user input.
9+
* Affected versions:
10+
* `0.9.9 - 0.12.2`
11+
* `0.20.0b1 - 0.22.0 (latest)`
12+
13+
### ✨ Breaking changes
14+
15+
* Drop support for Python 3.9
16+
17+
### 🐛 Fixes
18+
19+
* Fix selecting data with nested models with json fields [#1530](https://github.com/collerek/ormar/pull/1530)
20+
* Fix prefetching JSON list field throwing TypeError - thanks @jannyware-inc [#1402](https://github.com/collerek/ormar/pull/1402)
21+
22+
323
## 0.22.0
424

5-
### 🐛 Breaking changes
25+
### Breaking changes
626

727
* **Migration from `databases` library to native async SQLAlchemy**
828

@@ -122,7 +142,7 @@
122142

123143
## 0.21.0
124144

125-
### 🐛 Breaking changes
145+
### Breaking changes
126146

127147
* Drop support for Python 3.8
128148
* Remove the possibility to exclude parents' fields in children models (discouraged as bad practice anyway)

ormar/queryset/queryset.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,8 +704,13 @@ async def _query_aggr_function(self, func_name: str, columns: List) -> Any:
704704
if func_name in ["sum", "avg"]:
705705
if any(not x.is_numeric for x in select_actions):
706706
raise QueryDefinitionError(
707-
"You can use sum and svg only with" "numeric types of columns"
707+
"You can use sum and svg only with numeric types of columns"
708708
)
709+
if any(x.field_name not in x.target_model.model_fields for x in select_actions):
710+
raise QueryDefinitionError(
711+
"You can use aggregate functions only on "
712+
"existing columns of the target model"
713+
)
709714
select_columns = [x.apply_func(func, use_label=True) for x in select_actions]
710715
expr = self.build_select_expression().alias(f"subquery_for_{func_name}")
711716
expr = sqlalchemy.select(*select_columns).select_from(expr) # type: ignore

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "ormar"
33

44
[tool.poetry]
55
name = "ormar"
6-
version = "0.22.1"
6+
version = "0.23.0"
77
description = "An async ORM with fastapi in mind and pydantic validation."
88
authors = ["Radosław Drążkiewicz <collerek@gmail.com>"]
99
license = "MIT"

tests/test_vulnerabilities/__init__.py

Whitespace-only changes.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
from typing import Optional
2+
3+
import ormar
4+
import pytest
5+
6+
from tests.lifespan import init_tests
7+
from tests.settings import create_config
8+
9+
base_ormar_config = create_config()
10+
11+
12+
class Category(ormar.Model):
13+
ormar_config = base_ormar_config.copy(tablename="categories")
14+
15+
id: int = ormar.Integer(primary_key=True)
16+
name: str = ormar.String(max_length=100)
17+
18+
19+
class Item(ormar.Model):
20+
ormar_config = base_ormar_config.copy(tablename="items")
21+
22+
id: int = ormar.Integer(primary_key=True)
23+
name: str = ormar.String(max_length=100)
24+
price: float = ormar.Float(default=0)
25+
category: Optional[Category] = ormar.ForeignKey(Category, nullable=True)
26+
27+
28+
create_test_database = init_tests(base_ormar_config)
29+
30+
31+
@pytest.mark.asyncio
32+
async def test_arbitrary_sql_execution():
33+
async with base_ormar_config.database:
34+
async with base_ormar_config.database.transaction(force_rollback=True):
35+
if not await Item.objects.count():
36+
cat = await Category.objects.create(name="Electronics")
37+
await Item.objects.create(name="Tablet", price=449.99, category=cat)
38+
await Item.objects.create(name="Monitor", price=329.99, category=cat)
39+
40+
column = "1 + 1"
41+
with pytest.raises(ormar.exceptions.QueryDefinitionError):
42+
await Item.objects.min(column)
43+
44+
45+
@pytest.mark.asyncio
46+
async def test_arbitrary_sql_execution_on_related_model():
47+
async with base_ormar_config.database:
48+
async with base_ormar_config.database.transaction(force_rollback=True):
49+
if not await Item.objects.count():
50+
cat = await Category.objects.create(name="Electronics")
51+
await Item.objects.create(name="Tablet", price=449.99, category=cat)
52+
await Item.objects.create(name="Monitor", price=329.99, category=cat)
53+
54+
column = "category__1 + 1"
55+
with pytest.raises(ormar.exceptions.QueryDefinitionError):
56+
await Item.objects.min(column)
57+
58+
59+
@pytest.mark.asyncio
60+
async def test_schema_extraction():
61+
async with base_ormar_config.database:
62+
async with base_ormar_config.database.transaction(force_rollback=True):
63+
if not await Item.objects.count():
64+
cat = await Category.objects.create(name="Electronics")
65+
await Item.objects.create(name="Laptop", price=999.99, category=cat)
66+
67+
column = "(SELECT GROUP_CONCAT(name) FROM sqlite_master WHERE type='table')"
68+
with pytest.raises(ormar.exceptions.QueryDefinitionError):
69+
await Item.objects.min(column)

0 commit comments

Comments
 (0)