fix(#190): экранировать формулы в CSV-выгрузке качества (T15.1)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
1d99ea265e
commit
3589f53c3c
@ -354,6 +354,9 @@ class QualityMetricsService:
|
||||
return buffer.getvalue().encode("utf-8-sig"), "text/csv; charset=utf-8", "csv"
|
||||
|
||||
|
||||
_FORMULA_PREFIXES = ("=", "+", "-", "@", "\t", "\r")
|
||||
|
||||
|
||||
def _cell(value: Any) -> Any:
|
||||
"""Значение ячейки CSV: перечисления — их значение, даты — ISO, булевы — true/false."""
|
||||
if isinstance(value, bool):
|
||||
@ -362,4 +365,6 @@ def _cell(value: Any) -> Any:
|
||||
return value.value
|
||||
if isinstance(value, datetime | date):
|
||||
return value.isoformat()
|
||||
if isinstance(value, str) and value.startswith(_FORMULA_PREFIXES):
|
||||
return "'" + value # защита от CSV-инъекции формул в Excel
|
||||
return value
|
||||
|
||||
@ -30,7 +30,7 @@ from src.models.enums import (
|
||||
)
|
||||
from src.repositories.quality_facts import insert_failure_fact, insert_intake_fact, insert_outcome_fact
|
||||
from src.repositories.users import UserWithAccess
|
||||
from src.services.quality_metrics_service import EXPORT_COLUMNS
|
||||
from src.services.quality_metrics_service import EXPORT_COLUMNS, _cell
|
||||
from tests.conftest import AuthClientFactory
|
||||
|
||||
HTTP_OK, HTTP_FORBIDDEN, HTTP_UNPROCESSABLE = 200, 403, 422
|
||||
@ -322,3 +322,15 @@ async def test_quality_metrics_export(auth_client: AuthClientFactory, seeded: As
|
||||
assert rows[0]["is_scan"] == "true"
|
||||
as_json = await client.get(f"{BASE}/export", params={"format": "json", "date_from": "2026-11-01"})
|
||||
assert len(as_json.json()) == 2 # noqa: PLR2004
|
||||
|
||||
|
||||
@pytest.mark.parametrize("prefix", ["=", "+", "-", "@", "\t", "\r"])
|
||||
def test_csv_cell_escapes_formula_strings(prefix: str) -> None:
|
||||
assert _cell(f"{prefix}SUM(A1)") == f"'{prefix}SUM(A1)"
|
||||
|
||||
|
||||
def test_csv_cell_keeps_non_strings_and_plain_strings() -> None:
|
||||
assert _cell(-5) == -5 # noqa: PLR2004
|
||||
assert _cell("ООО Альфа") == "ООО Альфа"
|
||||
assert _cell(datetime.date(2026, 11, 1)) == "2026-11-01"
|
||||
assert _cell(True) == "true"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user